在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 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.

以下示例说明了这种情况的工作原理:

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.