Как предотвратить специализацию шаблона С++?

Компилятор не жалуется, когда я это делаю; -)

// 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...