nav
nav

Reputation: 1655

How to prevent specialization of a C++ template?

The compiler doesn't complain when I do this ;-)

// Myfile.h
#include <iostream>
#include <vector>

namespace std
{

template<> class vector<int>
{
public:
    vector ()
    {
        std::cout << "Happy Halloween !!!\n";
    }
};

}

Is there any way to prevent this kind of undesirable specialization of a class/function template?

--EDIT--

I just used std:: as an example. What I'm looking for is a way to prevent this from happening to any template class.

Upvotes: 18

Views: 5829

Answers (6)

Jan Herrmann
Jan Herrmann

Reputation: 2767

An alias template can not be specialized and has the behaviour you need for class templates.

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

In addition you should document that specialising my_class_implementation_which_should_not_be_used_directly results in undefined behavior. Now your libraries user can not specialize my_class accidentally and is warned about the class with the ugly name directly.

Upvotes: 7

Marco Veglio
Marco Veglio

Reputation: 332

The best way to prevent such behavior is through coding standards and code review.

You cannot force the compiler into an error in this case (other than using workarounds like those suggested in other answers), because that behavior is actually allowed by the language, although it's real usefulness can be questioned. The matter is - it is very possible that you want/need to provide a generic behavior for multiple types (hence the use of templates) but you need to provide a specific implementation for some of the allowed types (notably std::string)

A quick & dirty example (it's a function, but the same could apply to classes) could be the following:

template<typename TData> TData GetData(std::string argument)
{
    std::stringstream stream;
    TData retVal;
    stream.str(argument);
    stream >> retVal;
    return retVal;
}

However this is going to fail with std::string, as the >> operator would stop after the first blank space. So you could provide a dedicated specialization.

template<> std::string GetData(std::string argument)
{
    return argument;
}

Upvotes: 1

sehe
sehe

Reputation: 392911

What you do is specialize a standard library type inside a standard namespace.

Except for a few documented customization points (std::swap, std::hash<>) or specificly constrained specializations for User Defined Types (e.g. MySmartPtr<T>) this is against the specification and the result is undefined behaviour.


Edit: There is no mandatory diagnostic for this kind of rule violation.

To make it marginally harder for clients of your library to mess things up, you can do this trick:

namespace Public {


    namespace Hidden { // DON'T TOUCH THESE!
        template <typename> struct MyType { };
    }

    using Hidden::MyType;

}

Now, attempting to specialize MyType<> in namespace Hidden will fail.

Upvotes: 12

rpress
rpress

Reputation: 462

Edit:

You can prevent specializations of your templates with enable_if in C++11. This is impractical though.

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

link

main.cpp:26:16: error: no type named 'type' in 'struct std::enable_if<false, void>'

Upvotes: 1

user2922709
user2922709

Reputation: 67

There are a few ways to go about this. You can declare specific specializations without defining them. eg:

template<> struct MyStruct<int>;

That way, if someone tries to instantiate with an int, they would get this specialization and it won't compile because there is n definition. Then you can write a simple macro to do this for all the types you don't want a specialization for.

For the inverse of this, make the definition empty:

template<typename T> struct MyStruct{};

Then define the specific types you plan to support:

template<> struct MyStruct<int> {...};
template<> struct MyStruct<std::string> {...};
//etc...

Upvotes: -2

Mark B
Mark B

Reputation: 96243

No, the C++ language does not provide a general mechanism by which you can say "don't allow specializations of this template".

But it may not matter. For any instantiation that your code uses already, a user provided specialization will violate the one definition rule and their program may blow up in a fireball.

If you aren't using the instantiation in your library then what they do doesn't matter.

This is one of the cases where in C++ you simply can't prevent your user from shooting themself in the foot and if they choose to do so the responsibility is on them.

Upvotes: 8

Related Questions