In C++ template copy assignment operator not compatible with initializer_list?

吃可爱长大的小学妹 提交于 2019-12-24 03:29:37

问题


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

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&) {}

Why the template version is not compatible with the initializer_list?


回答1:


Because initializer list is a non-deduced context. From [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 ]

There are some cases, however, where you can still pass in an initializer list to a template. From [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.

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> ) { }

Or:

template <typename T, size_t N>
void operator=(T const(&)[N]) { }

Although the latter apparently doesn't compile on clang, incorrectly.




回答2:


Change your template version to

template <typename ValueType>
void operator =(const std::initializer_list<ValueType> &) {}


来源:https://stackoverflow.com/questions/31258811/in-c-template-copy-assignment-operator-not-compatible-with-initializer-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!