Специализация шаблона C ++ без функции по умолчанию - PullRequest
27 голосов
/ 27 октября 2009

У меня есть следующий код, который компилируется и работает хорошо:

template<typename T>
T GetGlobal(const char *name);

template<>
int GetGlobal<int>(const char *name);

template<>
double GetGlobal<double>(const char *name);

Однако я хочу удалить функцию «по умолчанию». То есть я хочу сделать все вызовы GetGlobal , где 't' не является целым числом или двойной ошибкой.

Например, GetGlobal () должен быть ошибкой времени компиляции.

Я пытался просто удалить функцию по умолчанию, но, как я себе представлял, я получил много ошибок ... Так есть ли способ "отключить" ее и разрешить вызовы только для специализированных версий функции?

Спасибо!

Ответы [ 5 ]

25 голосов
/ 27 октября 2009

Чтобы получить ошибку во время компиляции, используйте ее как:

template<typename T>
T GetGlobal(const char *name) { T::unimplemented_function; }
// `unimplemented_function` identifier should be undefined

Если вы используете Boost, вы можете сделать его более элегантным:

template<typename T>
T GetGlobal(const char *name) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }

C ++ Standard гарантирует, что не существует такого типа, размер которого равен 0, поэтому вы получите ошибку во время компиляции.

Как sbi предположил в своих комментариях, последнее может быть уменьшено до:

template<typename T>
T GetGlobal(const char *name) { char X[!sizeof(T)]; }

Я предпочитаю первое решение, потому что оно дает более четкое сообщение об ошибке (по крайней мере, в Visual C ++), чем другие.

15 голосов
/ 02 июня 2016

Хотя это старый и устаревший вопрос, стоит отметить, что C++11 решил эту проблему с помощью удаленных функций:

template<typename T>
T GetGlobal(const char *name) = delete;

template<>
int GetGlobal<int>(const char *name);

UPDATE

Это не скомпилируется в MacOS llvm 8. Это связано с еще 4-летним дефектом (см. отчет об ошибке ).

Следующий обходной путь подойдет для этой проблемы (с использованием конструкции static_assert).

template<typename T>
T GetGlobal(const char *name) {
    static_assert(sizeof(T) == 0, "Only specializations of GetGlobal can be used");
}

template<>
int GetGlobal<int>(const char *name);

UPDATE

Visual Studio 15.9 имеет ту же ошибку. Используйте предыдущий обходной путь для этого.

5 голосов
/ 27 октября 2009

Если вы не реализуете его, вы по крайней мере получите ошибку компоновщика. Если вам нужна ошибка времени компиляции, вы можете сделать это с помощью шаблонов классов:

template<typename T>
struct GlobalGetter;

template<>
struct GlobalGetter<int> {
  static int GetGlobal(const char *name);
};

template<>
struct GlobalGetter<double> {
  static double GetGlobal(const char *name);
};

template<typename T>
T GetGlobal(const char *name)
{
  return GlobalGetter<T>::GetGlobal(name);
}
3 голосов
/ 27 октября 2009

Я бы предложил на самом деле не предоставлять реализацию, просто голое объявление метода.

Другим вариантом будет использование assert во время компиляции. У Boost есть несколько таких зверей.

namespace mpl = boost::mpl;
BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>,
                            boost::same_type<T, int> >));

Существует также аналог версии сообщения, который поможет.

2 голосов
/ 27 октября 2009

Ниже приведены альтернативные методы использования наддува:

Объявление typedef для зависимого имени

Это работает, потому что поиск имени для DONT происходит только после замены 'T'. Это аналогичная (но легальная) версия примера, приведенного Кирилл

template <typename T>
T GetGlobal (const char * name) {
    typedef typename T::DONT CALL_THIS_FUNCTION;
}

Использовать неполный тип возврата

Этот метод не работает для специализаций, но он будет работать для перегрузок. Идея состоит в том, что можно объявить функцию, которая возвращает неполный тип, но не вызывать ее:

template <typename T>
class DONT_CALL_THIS_FUNCTION GetGlobal (const char * name);
...