R. Absil
R. Absil

Reputation: 243

Template substitution error with initializer list

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

Answers (2)

AndyG
AndyG

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

bruno
bruno

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

Related Questions