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

StackOverflow https://stackoverflow.com/questions/1629406

  •  06-07-2019
  •  | 
  •  

Вопрос

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

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' не является целым числом или двойной ошибкой.

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

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

Спасибо!

Это было полезно?

Решение

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

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++ гарантирует, что не существует такого типа, у которого sizeof равен 0, поэтому вы получите ошибку времени компиляции.

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

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

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

Другие советы

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

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

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

ОБНОВЛЯТЬ

Это не будет компилироваться под 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);

ОБНОВЛЯТЬ

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

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

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);
}

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

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

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

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

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

Объявите 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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top