Могу ли я частично скомпилировать функцию шаблона в С++
У меня есть функция, чтобы определить, является ли тип шаблона указателем.
template<class T>
struct is_pointer_struct { static const bool value = false; };
template<class T>
struct is_pointer_struct<T*> { static const bool value = true; };
template<class T>
bool is_pointer(T &var) {
return is_pointer_struct<T>::value;
}
И у меня есть функция инициализации.
template<class T>
void initialize(T &val) {
if (is_pointer(val))
val = NULL;
else
val = T();
}
Очевидно, что если T
- string
, этот код не может быть скомпилирован. Есть ли способ, который компилирует val = NULL
, когда T
является типом указателя и компилируется val = T()
, когда T
не является типом указателя?
Ответы
Ответ 1
В вашем конкретном случае вы можете просто использовать единую инициализацию, поскольку VTT сказал:
val = T{};
Кроме того, стандартная библиотека предоставляет std::is_pointer
.
Как ответ на более общий вопрос: "Как я могу во время компиляции?":
-
В С++ 17 все, что вам нужно сделать, это изменить ваш if(...)
на if constexpr(...)
:
template<class T>
void initialize(T &val) {
if constexpr(is_pointer(val))
val = nullptr;
else
val = T();
}
-
В С++ 14 вы можете реализовать свой собственный static_if
.
-
В С++ 03/11 вы можете использовать диспетчеризацию тегов:
template <typename T>
void initialize_impl(std::true_type /* pointer */, T& val)
{
val = NULL;
}
template <typename T>
void initialize_impl(std::false_type /* non-pointer */, T& val)
{
val = T();
}
template<class T>
void initialize(T &val) { initialize_impl(std::is_pointer<T>{}, val); }
Ответ 2
Правильный способ сделать вещи в вашем случае - использовать равномерную инициализацию, как упоминалось.
В качестве опции вы можете использовать SFINAE на основе вашего типа, чтобы создать экземпляр необходимого шаблона (здесь это способ С++ 11):
template<class T>
auto initialize(T &val) ->
typename std::enable_if<is_pointer_struct<T>::value>::type {
val = nullptr;
}
template<class T>
auto initialize(T &val) ->
typename std::enable_if<!is_pointer_struct<T>::value>::type {
val = T();
}
Ответ 3
Классическое решение, даже не требующее возможностей С++ 11: Простые перегрузки:
template<class T>
void initialize(T& val)
{
val = T();
}
template<class T>
void initialize(T*& val)
{
val = NULL;
}
Однако первая перегрузка (в конкретном случае) также охватывает указатели, поэтому вторая на самом деле устарела.
Не считая устаревания, я бы предпочел nullptr
ключевое слово поверх макроса NULL
(хотя и потерял совместимость с предшествующим С++ 11).