4

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 ?

R. Absil
  • 173
  • 6
  • Possible duplicate of [initializer\_list and template type deduction](https://stackoverflow.com/questions/12431495/initializer-list-and-template-type-deduction) – dewaffled Feb 22 '19 at 14:20
  • The post says "because the standard says so". I find it rather unsatisfying. I would like to know why. I'm not sure I understand why substitution fails – R. Absil Feb 22 '19 at 14:30
  • `{}` is never deduced to an `initializer_list` when passed to a template function (other than `auto`). you have to exclicitly provide the type, the compiler doesn't know you want an `initializer_list`. this is the standard, so there is no other answer. Never pass `{}` to a function with template arguments, it will never compile. – local-ninja Feb 22 '19 at 14:38

2 Answers2

2

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.

Community
  • 1
  • 1
AndyG
  • 39,700
  • 8
  • 109
  • 143
  • Ok, I understand. It's still "because the standard says so", but at least now I know why. I guess I'll just look into aggregates. – R. Absil Feb 22 '19 at 14:41
0

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      
bruno
  • 32,421
  • 7
  • 25
  • 37
  • No. I think I expalined wrongly what I want. I want the syntax written in my mai to work as it is written. I simply don't understand why the template code fails to work. – R. Absil Feb 22 '19 at 14:24
  • The post says "because the standard says so". I find it rather unsatisfying. I would like to know why – R. Absil Feb 22 '19 at 14:29
  • @R.Absil `error: could not convert ‘{4, 5, 6}’ from ‘’ to ‘Printer’` is not just unsatisfying ^^ How the compiler can guess what is the Container if you do not say ? – bruno Feb 22 '19 at 14:31
  • Well, it does it with a vector, I would assume that anything pack within {} is initializer_list, no ? – R. Absil Feb 22 '19 at 14:38