在与 initializer_list 不兼容的 C++ 模板复制赋值运算符中?
In C++ template copy assignment operator not compatible with initializer_list?
假设我有这样的代码:
#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;
}
第一个复制赋值运算符可以通过 instance = {1, 2}
正常编译。但是,模板版本将失败并出现以下错误:
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 不兼容?
因为初始化列表是一个非推导的上下文。来自 [temp.deduct.type]:
The non-deduced contexts are:
— [...]
— A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter
does not have a type for which deduction from an initializer list is specified (14.8.2.1). [ Example:
template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T
—end example ]
但是,在某些情况下,您仍然可以将初始化列表传递给模板。来自 [temp.deduct.call]
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 P
is a dependent type,
removing references and cv-qualifiers from P
gives std::initializer_list<P'>
or P'[N]
for some P'
and
N
and the argument is a non-empty 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, and in the P'[N]
case, if N
is a non-type template parameter, N
is deduced from the length of the initializer list.
下面的示例说明了这种方法适用的情况:
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
所以在你的具体情况下,你可以这样做:
template <typename T>
void operator=(std::initializer_list<T> ) { }
或:
template <typename T, size_t N>
void operator=(T const(&)[N]) { }
虽然后者显然不能在 clang 上编译,但错误。
将您的模板版本更改为
template <typename ValueType>
void operator =(const std::initializer_list<ValueType> &) {}
假设我有这样的代码:
#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;
}
第一个复制赋值运算符可以通过 instance = {1, 2}
正常编译。但是,模板版本将失败并出现以下错误:
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 不兼容?
因为初始化列表是一个非推导的上下文。来自 [temp.deduct.type]:
The non-deduced contexts are:
— [...]
— A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have a type for which deduction from an initializer list is specified (14.8.2.1). [ Example:template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T
—end example ]
但是,在某些情况下,您仍然可以将初始化列表传递给模板。来自 [temp.deduct.call]
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.
下面的示例说明了这种方法适用的情况:
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
所以在你的具体情况下,你可以这样做:
template <typename T>
void operator=(std::initializer_list<T> ) { }
或:
template <typename T, size_t N>
void operator=(T const(&)[N]) { }
虽然后者显然不能在 clang 上编译,但错误。
将您的模板版本更改为
template <typename ValueType>
void operator =(const std::initializer_list<ValueType> &) {}