在C++模板中,复制赋值运算符与initializer_list不兼容?

jay*_*ubi 2 c++ templates initializer-list c++11

考虑我有这样的代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

第一个复制赋值运算符可以编译为OK 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&) {}
Run Code Online (Sandbox Code Playgroud)

为什么模板版本与initializer_list不兼容?

Bar*_*rry 5

因为初始化列表是非推断的上下文.来自[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
Run Code Online (Sandbox Code Playgroud)

- 末端的例子]

但是,在某些情况下,您仍然可以将初始化列表传递给模板.来自[temp.deduct.call]

模板参数推导是通过将每个函数模板参数类型(调用它P)与调用的相应参数的类型(调用它)进行比较来完成的,A如下所述.如果P是一个依赖型,去除引用和CV -qualifiers从Pstd::initializer_list<P'>P'[N]用于某些P'N和所述参数是一个非空的初始化列表(8.5.4),然后进行扣除,而不是用于初始化列表中的每个元素,以P'作为一个函数模板参数类型和初始化元素作为其参数,在这种P'[N]情况下,if N是非类型模板参数,N是从初始化程序列表的长度推导出来的.

以下示例说明了其工作原理:

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
Run Code Online (Sandbox Code Playgroud)

因此,在您的具体情况下,您可以执行以下操作:

template <typename T>
void operator=(std::initializer_list<T> ) { }
Run Code Online (Sandbox Code Playgroud)

要么:

template <typename T, size_t N>
void operator=(T const(&)[N]) { }
Run Code Online (Sandbox Code Playgroud)

虽然后者显然不能在clang上编译,但不正确.