Variadic模板无法使用初始化列表

时间:2016-12-30 18:40:46

标签: c++ c++11 variadic-templates variadic-functions

我创建了一个工厂功能模板:

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

一个简单的容器:

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

然后我尝试创建一个组

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

这不会编译,

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)
                   ^

但如果我使用临时变量:

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

确实如此。为什么呢?

这种情况的推荐解决方案是什么?

2 个答案:

答案 0 :(得分:4)

模板参数不能从初始化列表中推导出来(它是非推导的上下文),但可以来自类型std::initializer_list<something>的表达式。两者不一样。

  

[temp.deduct.call] / 1 模板参数推断是通过将每个函数模板参数类型(称为P)与调用的相应参数的类型进行比较来完成的(如下所述,称之为A)。如果从P中删除引用和cv限定符,为std::initializer_list<P'>提供P',并且参数是初始化列表(8.5.4),则会对初始化程序的每个元素执行推导list,将P'作为函数模板参数类型,将initializer元素作为其参数。否则,初始化列表参数会将参数视为非推导上下文(14.8.2.5)。 [示例:

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
     

- 示例]

答案 1 :(得分:0)

来自cppreference

  

braced-init-list 不是表达式,因此没有类型,例如decltype({1,2})格式不正确。没有类型意味着模板类型推导不能推断出与 braced-init-list 匹配的类型,因此在声明template<class T> void f(T);的情况下,表达式f({1,2,3})是不正确的。 / p>

但有一个例外:auto a = { 1, 2 };导致a成为std::initializer_list。但这仅适用于auto类型推导,而不适用于模板。