Reputation: 243
I know how to make the following code work : I just to uncomment the second constructor of Printer.
The idea is simple : I want to write a constructor / function that can take several arguments stored in some abract data structure that I can iterate. I want it to work at least with vectors and lists (it does) but also with initializer list (and it doesn't).
I use the following simple syntax (maybe a bit more generic than I would like, I don't use template templates) so I don't have to write a variadic template in order to handle the allocator types of std::
#include <iostream>
#include <vector>
#include <list>
#include <initializer_list>
using namespace std;
struct Printer
{
template<class Container>
Printer(const Container& cont)
{
for(const auto & e : cont)
cout << e << " ";
cout << endl;
}
//template<class T>
//Printer(const initializer_list<T> & l)//why should I ?
//{
// for(const T & e : l)
// cout << e << " ";
// cout << endl;
//}
};
int main()
{
vector<int> v = {1,2,3};
Printer pv(v);
Printer pv2 = v; //ok : not explicit
list<char> l = {'a', 'b', 'c'};
Printer pl(l);
Printer pl2 = l; //ok : not explicit
Printer pi1 = {4,5,6};
Printer pi2 ({4,5,6}); //same as above if explicit
}
But why should I explicitely write a specific constructor for initializer list ? The error is "could not convert ‘{4, 5, 6}’ from ‘’ to ‘Printer’".
Basically, what it tells is that the substitution does not work with the initializer list. But why ?
Upvotes: 4
Views: 484
Reputation: 41100
A brace-enclosed initializer list (formally, braced-init-list) is not a std::initializer_list
. It can be converted to one, but it isn't one. It's also not a container type. It's not a type at all, actually, it's a grammatical construct consisting of either the following sequences of symbols:
{ initializer-list ,opt }
{ }
Therefore this syntax will not work outright:
Printer pi1 = {4,5,6};
Printer pi2 ({4,5,6}); //same as above if explicit
If Printer
was an aggregate, then it could perform aggregate initialization.
Otherwise, you're kind of stuck accepting a std::initializer_list
that can be constructed from the brace-enclosed iniitalizer list you've provided.
More info:
What you're actually doing with that syntax is called list-initialization, (aggregate initialization is a type of list-initialization). Just to confuse things further, when used to initialize a type like T{a, b, c, ...}
it's called an initializer list. This is not to be confused with a std::initializer_list
.
When std::initializer_list
was added in C++11, it got special treatment. A braced-init-list could now be used to construct a temporary std::initializer_list
in a constructor. This is where you saw us suddenly able to create a std::vector<int>
so easily as std::vector<int> vec{1, 2, 3, 4, 5, ...};
However, one thing to be wary of is that std::initializer_list
constructors are "high priority" constructors that the compiler will choose when you least suspect.
Upvotes: 2
Reputation: 32596
Replace
Printer pi1 = {4,5,6};
Printer pi2 ({4,5,6}); //same as above if explicit
by something like
Printer pi1 = vector<int>{4,5,6};
Printer pi2 (vector<int>{4,5,6}); //same as above if explicit
Upvotes: 0