Initializer list issue in constructor

226 views Asked by At

I have a hard time understanding how std::initializer_list works. I checked other questions, but found nothing relevant (or maybe I didn't see it?).

Say I have this:

template<typename T> 
struct Point
{
    T x,y;
};

template<typename T> 
struct A
{
    std::vector<Point<T>> v;
};

Then I can build with:

int main()
{
   A<int> a{ std::vector<Point<int>> { {4,4}, {5,5},{6,6} } };
}

But I'd like to make thing simpler, so I can write:

int main()
{
   A<int> a( { {4,4}, {5,5},{6,6} } );
}

I tried:

template<typename T> 
struct A
{
    std::vector<Point<T>> v;
    template<typename U>
    A( const std::initializer_list<Point<U>>& il ) : v{il}
    {}
};

But this fails, see live demo.

How can I write a constructor allowing this? Is this even possible?

3

There are 3 answers

1
HolyBlackCat On BEST ANSWER

First of all, your desired syntax almost works (without adding a constructor to A), just replace (...) with {...}.

int main()
{
   A<int> a{{ {4,4}, {5,5},{6,6} }};
}

Second of all, if you still want to write a constructor for A don't use std::initializer_list for it! std::vector already does it, so you don't have to. Just pass the vector:

template<typename T> 
struct A
{
    std::vector<Point<T>> v;
    A(std::vector<Point<T>> v) : v(std::move(v)) {}
};

int main()
{
    A<int> a( { {4,4}, {5,5},{6,6} } );
}
1
Jarod42 On

Your constructors should not be template, your class is already. {..} has no type and can be deduced to very few types.

Once removed, following works:

template<typename T> 
struct A
{
    std::vector<Point<T>> v;

    A( const std::vector<Point<T>>& in ) : v(in) {}

    A( const std::initializer_list<Point<T>>& il ): v{il}
    {}
};

int main()
{
    std::vector<Point<int>> v{ {4,4}, {5,5},{6,6} };
    A<int> a1( std::vector<Point<int>> { {4,4}, {5,5},{6,6} } ); // this is fine
    assert( a1.v.size() == 3 );

    A<int> a2{{4,4}, {5,5}, {6,6} };
    assert( a2.v.size() == 3 );

    A<int> a3({{4,4}, {5,5}, {6,6} });
}

Demo

1
user12002570 On

With C++20 there is a feature called parenthesized initialization of aggregates which allows your given syntax to be well-formed.

template<typename T> 
struct Point
{
    T x, y;
};

template<typename T> 
struct A
{
    std::vector<Point<T>> v;
    
};
int main()
{
//--------------v------------------------v---> parenthesis ok with c++20, replace () with {} for pre-c++20
        A<int> a( { {4, 4}, {5, 5}, {6, 6} } );
        
}

Working demo

This means that if you're working with pre-c++20, you can replace the parenthesis () with braces {}. demo c++14