Ferenc Deak
Ferenc Deak

Reputation: 35408

C++11: Variadic template deduction logic

I have the following construct:

template <class... Args>
class some_class
{
public:
    some_class() = default;
    some_class(Args...) = delete;
    ~some_class() = default;
};

template<>
class some_class<void>
{
public:
    some_class() = default;
    ~some_class() = default;
};

The reason for this is that I just want to allow the users to create objects using the default constructor, so for example:

some_class<int,float> b;

should work but

some_class<int,float> c(1,3.4);

should give me a compilation error.

At some point in time I also needed to create templates based on void hence, the specialization for void:

some_class<void> a;

But by mistake I have typed:

some_class<> d;

And suddenly my code stopped compiling and it gave me the error:

some_class<Args>::some_class(Args ...) [with Args = {}]’ cannot be 
overloaded
 some_class(Args...) = delete;

So here comes the question: I feel that I am wrong that I assume that some_class<> should be deduced to the void specialization... I just don't know why. Can please someone explain why some_class<> (ie: empty argument list) is different from some_class<void>? (A few lines from the standard will do :) )

https://ideone.com/o6u0D6

Upvotes: 2

Views: 97

Answers (1)

void is a type like any other (an incomplete type, to be precise). This means it can be used as a template argument for type template parameters normally. Taking your class template, these are all perfectly valid, and distinct, instantiations:

some_class<void>
some_class<void, void>
some_class<void, void, void>
some_class<void, char, void>

In the first case, the parameter pack Args has one element: void. In the second case, it has two elements: void and void. And so on.

This is quite different from the case some_class<>, in which case the parameter pack has zero elements. You can easily demonstrate this using sizeof...:

template <class... Pack>
struct Sizer
{
  static constexpr size_t size = sizeof...(Pack);
};

int main()
{
  std::cout << Sizer<>::size << ' ' << Sizer<void>::size << ' ' << Sizer<void, void>::size << std::endl;
}

This will output:

0 1 2

[Live example]

I can't really think of a relevant part of the standard to quote. Perhaps this (C++11 [temp.variadic] 14.5.3/1):

A template parameter pack is a template parameter that accepts zero or more template arguments. [ Example:

template<class ... Types> struct Tuple { };
Tuple<> t0; // Types contains no arguments
Tuple<int> t1; // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float
Tuple<0> error; // error: 0 is not a type

—end example ]

Upvotes: 2

Related Questions