I had a problem in a highly templated code, and I isolated it in this example program (I compile with g++ 4.7.1) :
#include <iostream>
#include <initializer_list>
#include <type_traits>
#define OPTION 2
// Test class
template<typename T = double>
class MyClass
{
public:
// Constructors
MyClass(const MyClass<T>& source)
{std::cout<<"copy constructor"<<std::endl;}
#if OPTION == 1
template<typename T2 = T>
MyClass(const std::initializer_list<T2>& source)
{std::cout<<"init constructor"<<std::endl;} // OPTION 1
#elif OPTION == 2
template<typename T2 = T, class... Misc>
MyClass(const std::initializer_list<T2>& source, const Misc&... misc)
{std::cout<<"init+misc constructor"<<std::endl;} // OPTION 2
#endif
template<class... Misc>
explicit MyClass(const Misc&... misc)
{std::cout<<"misc constructor"<<std::endl;}
// Assignment
inline MyClass<T>& operator=(const MyClass<T>& rhs)
{std::cout<<"copy assignment"<<std::endl; return *this;}
template<class Misc>
inline MyClass<T>& operator=(const Misc& rhs)
{std::cout<<"misc assignment"<<std::endl; return *this;}
};
// Main
int main(int argc, char* argv[])
{
MyClass<double> x;
x = {4., 8., 15., 16., 23., 42.}; // CALL LINE
return 0;
}
The problem is the following. With OPTION == 1, all is ok, and the CALL LINE calls the “init constructor”.
But with OPTION == 2, I have the following error message :
error: converting to ‘const MyClass<double>’ from initializer list would use
explicit constructor ‘MyClass<T>::MyClass(const Misc& ...) [with Misc = {double,
double, double, double, double, double}; T = double]’
The question is : why ? Is it normal or is it a bug of g++ ?
Furthemore, I need a workaround : I have to implement the option 2 constructor without option 1. The most obvious thing for me would be to block the call to the explicit constructor with an enable if, like :
template<class... Misc, class std::enable_if<SOMETHING>::type>
explicit MyClass(const Misc&... misc)
{std::cout<<"misc constructor"<<std::endl;}
My problem is : what can I write instead of SOMETHING in order to block every std::initializer_list<T2> (for all T2 types) ?
To the first question, about the error message:
The constructor in
OPTION==2is not considered as initializer-list constructor, because such a constructor is required to either have a singlestd::initializer_list<E>parameter (or such a reference) or all other parameters shall have default arguments. (8.5.4 List-initialization [dcl.init.list] #2).Therefore, not finding an initializer-list constructor the compiler tries other constructors with argument list consisting of the elements of the initializer list, finds the explicit constructor and, since we’re in the context of implicit conversion (initializing the parameter of the assignment operator), fails with the error message.