Подтвердить что ты не робот

Специализация шаблона С++ без функции по умолчанию

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

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> () должно быть ошибкой времени компиляции.

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

Спасибо!

4b9b3361

Ответ 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);