简体   繁体   中英

Variadic template not working with an initializer list

I created a factory function template:

template <typename M, typename... Args>
std::shared_ptr<M> create(Args... args)
{
    return std::make_shared<M>(args...);
}

And a simple container:

struct Group {
    std::vector<int> vec;
    Group(std::initializer_list<int> il) : vec(il) {}
};

Then I try to create a Group

int main()
{
    auto gr = create<Group>({1, 2, 3});
    return 0;
}

This doesn't compile,

error: no matching function for call to 'create'
    auto gr = create<Group>({1, 2, 3});
candidate function not viable: requires 0 arguments, but 1 was provided
std::shared_ptr<M> create(Args... args)
                   ^

but if I use a temporary variable:

int main(int argc, char *argv[])
{
    std::initializer_list<int> il = {1, 2, 3};
    auto gr = create<Group>(il);
    return 0;
}

it does. Why?

What is the recommended solution for such case?

A template parameter cannot be deduced from an initializer list (it's a non-deduced context), but can from an expression of type std::initializer_list<something> . The two are not the same.

[temp.deduct.call]/1 Template argument deduction is done by comparing each function template parameter type (call it P ) with the type of the corresponding argument of the call (call it A ) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P'> for some P' and the argument is an initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5). [ Example:

 template<class T> void f(std::initializer_list<T>); f({1,2,3}); // T deduced to int f({1,"asdf"}); // error: T deduced to both int and const char* template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T 

—end example ]

From cppreference :

A braced-init-list is not an expression and therefore has no type, eg decltype({1,2}) is ill-formed. Having no type implies that template type deduction cannot deduce a type that matches a braced-init-list , so given the declaration template<class T> void f(T); the expression f({1,2,3}) is ill-formed.

There is one exception though: auto a = { 1, 2 }; results in a being a std::initializer_list . But this is only in auto type deducing, not for templates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM