KaiserJohaan
KaiserJohaan

Reputation: 9240

Variadic template and copy constructors

Why does the following not compile:

struct a
{
    int i;
};


template <typename T>
class b
{
public:
    T mItem;


    template <typename... Arguments>
    b(Arguments&&... args) : mItem(std::forward<Arguments>(args)...)
    {
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    b<a>(1);

    return 0;
}

error C2664: 'a::a(const a &)' : cannot convert argument 1 from 'int' to 'const a &'

But simply adding an extra argument like this make it compile:

struct a
{
    int i;
};


template <typename T>
class b
{
public:
    T mItem;


    template <typename... Arguments>
    // just random extra argument
    b(int, Arguments&&... args) : mItem(std::forward<Arguments>(args)...)
    {
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    b<a>(1);

    return 0;
}

Is there a cleaner way than simply adding an extra (useless) argument to b's constructor?

Upvotes: 0

Views: 164

Answers (3)

TartanLlama
TartanLlama

Reputation: 65630

You are trying to rely on aggregate-initialization, but you need to use braces rather than parentheses to use it:

template <typename... Arguments>
b(Arguments&&... args) : mItem{std::forward<Arguments>(args)...}
{
}

The braces mean that you are using list-initialization rather than value-/direct-initialization. The former resolves to aggregate-initialization when T is an aggregate type, but the latter two do not. Yeah, C++ initialization rules are weird.

Upvotes: 5

SergeyA
SergeyA

Reputation: 62603

You are trying to initialize A with int. A does not know anything about int, there is no constructor in A which can do this. When you provide an extra int argument, you are just discarding it and than initialze A with empty argument list, calling default constructor.

Upvotes: -1

Petr
Petr

Reputation: 9997

In the first case you try to invoke

a(1)

which you have not.

In the second case, your 1 parameter is consumed by int and the rest parameters (that is, none) goes to args. Therefore, you invoke

a()

which is ok.

Note that in the latter case, the value of 1 which you passed to b<a>::b is lost and is not forwarded to a::i as you might have expected.

Upvotes: 1

Related Questions