简体   繁体   中英

Assignment of initializer list

The code below is a minimal example of my problem. I created a simple template class containing a fixed-size array, and overloaded the assignment operator to accept any class defining the methods size() and begin() (eg, initializer_list s). I don't understand why g++ is not able to resolve my call to this operator (I'm using gcc 4.6):

***.cpp: In function ‘int main()’:
***.cpp:46:22: error: no match for ‘operator=’ in ‘a = {42, -1.0e+0, 3.14158999999999988261834005243144929409027099609375e+0}’
***.cpp:46:22: note: candidates are:
***.cpp:23:8: note: template<class U> A<T, N>::self& A::operator=(const U&) [with U = U, T = double, unsigned int N = 3u, A<T, N>::self = A<double, 3u>]
***.cpp:8:7: note: A<double, 3u>& A<double, 3u>::operator=(const A<double, 3u>&)
***.cpp:8:7: note:   no known conversion for argument 1 from ‘<brace-enclosed initialiser list>’ to ‘const A<double, 3u>&’
***.cpp:8:7: note: A<double, 3u>& A<double, 3u>::operator=(A<double, 3u>&&)
***.cpp:8:7: note:   no known conversion for argument 1 from ‘<brace-enclosed initialiser list>’ to ‘A<double, 3u>&&’

The first candidate is listed correctly, but there is no error message associated. Here is the code:

#include <iostream>
#include <algorithm>
#include <initializer_list>

// ------------------------------------------------------------------------

template <typename T, unsigned N>
class A
{
public:

    typedef A<T,N> self;

    // Default ctor
    A() {}

    // Copy ctor
    template <typename U>
    A( const U& other ) { operator=(other); }

    // Assignemnt
    template <typename U>
    self& operator= ( const U& other )
    {
        if ( other.size() == N )
            std::copy_n( other.begin(), N, m_data );
            return *this;
    }

    // Display contents
    void print() const
    {
        for ( unsigned i = 0; i < N; ++i )
            std::cout << m_data[i] << " ";
        std::cout << std::endl;
    }

private:
    T m_data[N];
};

// ------------------------------------------------------------------------

int main()
{
    A<double,3> a;
    a = {42,-1.0,3.14159};
    a.print();
}

Does anyone know why this might be ambiguous, or what I did wrong?


Note: Ideally, I would even like to replace the first two lines of my main by a single one A<double,3> a = {42,-1.0,3.14159}; but I'm not sure how, I currently get the following error:

***: In function ‘int main()’:
***:45:34: error: could not convert ‘{42, -1.0e+0, 3.14158999999999988261834005243144929409027099609375e+0}’ from ‘<brace-enclosed initialiser list>’ to ‘A<double, 3u>’

Unlike auto , where a braced-init-list is deduced as an initializer_list , template argument deduction considers it to be a non-deduced context, unless there exists a corresponding parameter of type initializer_list<T> , in which case the T can be deduced.

From §14.8.2.1/1 [temp.deduct.call] (emphasis added)

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 removing references and cv-qualifiers from P gives std::initializer_list<P0> for some P0 and the argument is an initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, taking P0 as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5).

Thus the argument to your operator= is not deduced to be an initializer_list<double> . For the code to work, you must define an operator= that takes an initializer_list argument.

template <typename U>
self& operator= ( const std::initializer_list<T>& other )
{
    if ( other.size() == N )
        std::copy_n( other.begin(), N, m_data );
    return *this;
}

A brace-enclosed initializer list does not necessarily have the type std::initializer_list<T> , so you need to specify that the assignment operator template expects an std::initializer_list :

  template <typename U>
  A& operator=(std::initializer_list<U> other )
  {
    if ( other.size() == N )
      std::copy_n( other.begin(), N, m_data );
    return *this;
  }

or

  A& operator=(std::initializer_list<double> other )
  {
    if ( other.size() == N )
      std::copy_n( other.begin(), N, m_data );
    return *this;
  }

I must say, an assignment operator that silently fails if the sizes don't match doesn't seem like a great idea.

I'd say it's this:

A<double,3> a;
a = {42,-1.0,3.14159};

You are initializing a with default constructor, and then trying to use initializer list on already initialized object - which complains about lacking of appropriate operator= overload. Instead try:

A<double,3> a = {42,-1.0,3.14159};

EDIT:

You also didn't defined required constructor:

template <typename T, unsigned N>
class A
{
public:
    A(std::initializer_list list) : m_data(list) {}

//...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM