Могу ли я частично скомпилировать функцию шаблона в С++

У меня есть функция, чтобы определить, является ли тип шаблона указателем.

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).