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 itA
) as described below. If removing references and cv-qualifiers fromP
givesstd::initializer_list<P0>
for someP0
and the argument is an initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, takingP0
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;
}