在C ++模板中,副本赋值运算符与initializer_list不兼容?
考虑一下我有这样的代码:
Consider I have such code:
#include <initializer_list>
class my_class
{
public:
my_class() {}
void operator = (const std::initializer_list<int>&) {} // OK
template<typename ValueType> void operator = (const ValueType&) {} // Failed
};
int main(int argc, char* argv[])
{
my_class instance;
instance = {1, 2};
return 0;
}
可以使用编译好第一个副本赋值运算符实例= {1,2}
。但是,模板版本将因以下错误而失败:
The first copy assignment operator could be compiled OK with instance = {1, 2}
. However, the template version would failed with such error:
code.cpp:15:14: error: no viable overloaded '='
instance = {1, 2};
~~~~~~~~ ^ ~~~~~~
code.cpp:3:7: note: candidate function (the implicit copy assignment operator) not viable: cannot convert initializer list argument to 'const my_class'
class my_class
^
code.cpp:3:7: note: candidate function (the implicit move assignment operator) not viable: cannot convert initializer list argument to 'my_class'
class my_class
^
code.cpp:9:39: note: candidate template ignored: couldn't infer template argument 'ValueType'
template<typename ValueType> void operator = (const ValueType&) {}
为什么模板版本与initializer_list不兼容?
Why the template version is not compatible with the initializer_list?
因为初始化列表是一个非推导的上下文。来自[temp.deduct.type]:
Because initializer list is a non-deduced context. From [temp.deduct.type]:
未推论的上下文是:
— [... ]
—一个函数参数,为其关联的参数为初始值设定项列表(8.5.4),但参数
不具有为其指定从初始值设定项列表中推论的类型(14.8。 2.1)。 [示例:
template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T
-结束示例]推论得出的参数]
但是,在某些情况下,您仍然可以将初始化列表传递给模板。通过[temp.deduct.call]
There are some cases, however, where you can still pass in an initializer list to a template. From [temp.deduct.call]
模板参数推导是通过比较每个函数模板参数类型(称为
P
),并带有
调用的相应自变量(称为A
)的类型,如下所述。如果P
是从属类型,则
从P $ c $中删除引用和 cv 限定词c>对于 std :: initializer_list< P'>
或P'[N]
c> P'和N
,并且参数为非空初始化列表(8.5.4),然后进行推导而是对于初始化程序列表的每个元素,将P'
作为函数模板参数类型,并将初始化程序元素作为
作为其参数,并在P'[N]
情况,如果N
是非类型模板参数,则N $ c从初始化列表的长度推导出$ c>。
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 itA
) as described below. IfP
is a dependent type, removing references and cv-qualifiers fromP
givesstd::initializer_list<P'>
orP'[N]
for someP'
andN
and the argument is a non-empty initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument, and in theP'[N]
case, ifN
is a non-type template parameter,N
is deduced from the length of the initializer list.
以下示例说明了这种情况的工作原理:
The examples that follow illustrate the cases in which this works:
template<class T> void f(std::initializer_list<T>);
f({1,2,3}); // T deduced to int
template<class T, int N> void h(T const(&)[N]);
h({1,2,3}); // T deduced to int, N deduced to 3
template<class T> void j(T const(&)[3]);
j({42}); // T deduced to int, array bound not considered
因此,在您的特定情况下,您可以执行一些操作
So in your specific case, you could do something like:
template <typename T>
void operator=(std::initializer_list<T> ) { }
或:
template <typename T, size_t N>
void operator=(T const(&)[N]) { }
尽管后者显然不能正确地在clang上编译。
Although the latter apparently doesn't compile on clang, incorrectly.