Как предотвратить специализацию шаблона С++?
Компилятор не жалуется, когда я это делаю; -)
// Myfile.h
#include <iostream>
#include <vector>
namespace std
{
template<> class vector<int>
{
public:
vector ()
{
std::cout << "Happy Halloween !!!\n";
}
};
}
Есть ли способ предотвратить такую нежелательную специализацию шаблона класса/функции?
- EDIT -
Я просто использовал std:: в качестве примера. Я ищу способ предотвратить это от любого класса шаблона.
Ответы
Ответ 1
шаблон псевдонима не может быть специализированным и имеет поведение, которое необходимо для шаблонов классов.
template<class T>
struct my_class_implementation_which_should_not_be_used_directly
{
// impl
};
template<class T>
using my_class = my_class_implementation_which_should_not_be_used_directly<T>;
Кроме того, вы должны подтвердить, что специализация my_class_implementation_which_should_not_be_used_directly
приводит к поведению undefined. Теперь пользователь ваших библиотек случайно не может специализировать my_class
и предупреждается о классе с уродливым именем dirctly.
Ответ 2
То, что вы делаете, специализируется на стандартном типе библиотеки внутри стандартного пространства имен.
За исключением нескольких задокументированных точек настройки (std:: swap, std:: hash < > ) или специфически ограниченных специализаций для пользовательских типов (например, MySmartPtr<T>
), это противоречит спецификации, а результат - undefined.
Изменить. Для этого нарушения правил нет обязательной диагностики.
Чтобы сделать это немного сложнее для клиентов вашей библиотеки, чтобы все испортить, вы можете сделать этот трюк:
namespace Public {
namespace Hidden { // DON'T TOUCH THESE!
template <typename> struct MyType { };
}
using Hidden::MyType;
}
Теперь попытка специализации MyType<>
в пространстве имен Hidden
завершится с ошибкой.
Ответ 3
Нет, язык С++ не предоставляет общий механизм, с помощью которого вы можете сказать "не разрешать специализации этого шаблона".
Но это не имеет значения. Для любого экземпляра, который ваш код использует уже, предоставленная специалистом специализация нарушит одно правило определения, и их программа может взорваться в огненном шаре.
Если вы не используете экземпляр в своей библиотеке, то то, что они делают, не имеет значения.
Это один из случаев, когда на С++ вы просто не можете помешать вашему пользователю стрелять в ногу, и если они захотят сделать это, ответственность будет за них.
Ответ 4
Edit:
Вы можете предотвратить специализацию ваших шаблонов с помощью enable_if в С++ 11. Однако это нецелесообразно.
#include <type_traits>
template<
typename real_t,
typename = typename std::enable_if<
std::is_floating_point<real_t>::value
>::type
>
struct check_t;
template<
typename real_t,
typename = typename
std::enable_if<
std::is_floating_point<real_t>::value,
check_t<real_t>
>::type
>
class vec_t
{
};
#if 1
template<>
class vec_t<int> {};
template<>
class vec_t<int,check_t<int>> {};
#endif
void test()
{
vec_t<float> vecf;
vec_t<int> veci;
}
ссылка
main.cpp:26:16: error: no type named 'type' in 'struct std::enable_if<false, void>'
Ответ 5
Лучший способ предотвратить такое поведение - через стандарты кодирования и обзор кода.
Вы не можете заставить компилятор ошибиться в этом случае (кроме использования обходных методов, подобных тем, которые предлагаются в других ответах), потому что это поведение действительно разрешено языком, хотя его реальная полезность может быть поставлена под сомнение.
Дело в том, что очень важно, чтобы вы хотели/должны были предоставлять общее поведение для нескольких типов (отсюда и использование шаблонов), но вам нужно предоставить конкретную реализацию для некоторых разрешенных типов (особенно std::string)
Быстрый и грязный пример (это функция, но то же самое может относиться к классам) может быть следующим:
template<typename TData> TData GetData(std::string argument)
{
std::stringstream stream;
TData retVal;
stream.str(argument);
stream >> retVal;
return retVal;
}
Однако это сработает с std::string, так как оператор → остановится после первого пустого пространства. Поэтому вы можете предоставить специальную специализацию.
template<> std::string GetData(std::string argument)
{
return argument;
}
Ответ 6
Есть несколько способов обойти это. Вы можете объявить конкретные специализации, не определяя их. например:
template<> struct MyStruct<int>;
Таким образом, если кто-то пытается создать экземпляр с помощью int, они получат эту специализацию, и она не будет компилироваться, потому что существует n определение. Затем вы можете написать простой макрос, чтобы сделать это для всех типов, для которых вам не нужна специализация.
Для обратного этого сделать определение пустым:
template<typename T> struct MyStruct{};
Затем определите конкретные типы, которые вы планируете поддерживать:
template<> struct MyStruct<int> {...};
template<> struct MyStruct<std::string> {...};
//etc...