I have this kind of structure for a template class.
My aim is to forbid creation of those class which not provide a full specialization:
class AbstractContainer
{
public:
virtual ~AbstractContainer() = default;
// declare here interface methods
};
template <class T>
class Container final : public AbstractContainer
{
public:
Container() = delete;
};
template <>
class Container<int> : public AbstractContainer
{
public:
Container() = default;
explicit Container(const int& type) : AbstractContainer(), type_(type){}
private:
int type_;
};
Everyhthing works fine
Container<int> a; // it works
Container<int> a(5); // it works
Container<char> a; // does not compile
but I noticed it compiles for these case
Container<int> a(Container<char>());
Container<int> a(Container<CustomClass>());
How can I avoid this situation? I do want a copy constructor but not with the wrong type, ideally I would like to have the same issue of compile error (I could use an assert somewhere, but don't know how to set up it).
This seems to be a C++'s most vexing parse issue:
Container<int> a(Container<char>());
This is actually just a function declaration: It declares a function a
that returns a Container<int>
object and takes a pointer to a function that returns Container<char>
and takes nothing.
Since it is just a function declaration, it does not instantiate Container<char>
at all and, therefore you are not getting any error.
You could use brace syntax instead of parenthesis, i.e.:
Container<int> a{Container<char>()};
Container<int> b(Container<char>{});
Container<int> c{Container<char>{}};
The code above does declare three objects: a
, b
and c
.
Container<>
's primary templateYou could define the following template, deferred_false
, as:
template<typename>
struct deferred_false {
static constexpr auto value = false;
};
or simply as:
#include <type_traits>
template<typename> struct deferred_false: std::false_type{};
Then, place a static_assert
that uses this deferred_false<T>::value
in the definition of your class template :
template<class T>
class Container final : public AbstractContainer
{
static_assert(deferred_false<T>::value, "Trying to instantiate Container<>");
};
This way, the assertion will fail at compile time when the primary template of Container
is going to be instantiated, but won't fail when the primary template is not being instantiated, since the condition of the static_assert
depends on the template parameter (i.e.: T
).
That is, if there is no specialization of Container
for Foo
and you have:
Container<Foo> a;
You will get the following compile-time error:
error: static assertion failed: Trying to instantiate
Container<>