可变参数模板不适用于初始值设定项列表

Variadic template not working with an initializer list

本文关键字:列表 适用于 参数 变参 不适用      更新时间:2023-10-16

我创建了一个工厂函数模板:

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;
}

确实如此。为什么?

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

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

[temp.deduct.call]/1模板参数推导是通过将每个函数模板参数类型(调用它P)与调用的相应参数的类型(调用它A)进行比较来完成的,如下所述。如果从P中删除引用和 cv 限定符会为某些P'提供std::initializer_list<P'>,并且参数是初始值设定项列表 (8.5.4),则改为对初始值设定项列表中的每个元素执行推导,将P'作为函数模板参数类型,将初始值设定项元素作为其参数。否则,初始值设定项列表参数会导致参数被视为非推导上下文 (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

—结束示例]

来自 cpp首选项:

大括号的初始化列表不是表达式,因此没有类型,例如decltype({1,2})格式不正确。没有类型意味着模板类型推导无法推断出与大括号初始化列表匹配的类型,因此给定声明template<class T> void f(T);表达式f({1,2,3})格式不正确。

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