Специализация шаблона С++ без функции по умолчанию
У меня есть следующий код, который компилируется и работает хорошо:
template<typename T>
T GetGlobal(const char *name);
template<>
int GetGlobal<int>(const char *name);
template<>
double GetGlobal<double>(const char *name);
Однако я хочу удалить функцию "по умолчанию". То есть, я хочу сделать все вызовы GetGlobal <t> где 't' не является int или двойной ошибкой.
Например, GetGlobal <char> () должно быть ошибкой времени компиляции.
Я попытался просто удалить функцию по умолчанию, но, как я себе представлял, я получил много ошибок. Так есть способ "отключить" его и разрешить вызовы только специализированным версиям функции?
Спасибо!
Ответы
Ответ 1
Чтобы получить ошибку времени компиляции, выполните ее как:
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); }
Стандарт С++ гарантирует, что такого типа нет, размер которого равен 0, поэтому вы получите ошибку времени компиляции.
Как sbi, предложенный в его комментариях, последний может быть сведен к:
template<typename T>
T GetGlobal(const char *name) { char X[!sizeof(T)]; }
Я предпочитаю первое решение, потому что оно дает более четкое сообщение об ошибке (по крайней мере, в Visual С++), чем другие.
Ответ 2
Хотя это старый и устаревший вопрос, может быть стоит отметить, что 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);
Ответ 3
Если вы его не реализуете, вы, по крайней мере, получите ошибку компоновщика. Если вам нужна ошибка времени компиляции, вы можете сделать это с помощью шаблонов классов:
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);
}
Ответ 4
Я бы предложил не предоставлять реализацию, а просто объявить метод.
Другой вариант - использовать утверждение времени компиляции. У Boost есть множество таких зверей.
namespace mpl = boost::mpl;
BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>,
boost::same_type<T, int> >));
Существует также его версия версии сообщений, которая поможет.
Ответ 5
Ниже приведены альтернативные методы использования boost:
Объявить 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);