Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the correct way to initialize a member array with an initializer list?

Tags:

c++

c++11

I have a struct that holds an array, and I'd like to pass an initializer list to the struct's constructor to be forwarded on to the array. To illustrate, I tried:

#include <initializer_list>

struct Vector
{
    float v[3];

    Vector(std::initializer_list<float> values) : v{values} {}
};

int main()
{
    Vector v = {1, 2, 3};
}

Which resulted in the error:

error: cannot convert ‘std::initializer_list<float>’ to ‘float’ in initialization

I tried using parentheses instead of braces for v but that gave the error:

error: incompatible types in assignment of ‘std::initializer_list<float>’ to ‘float [3]’

My main motivation for trying to do this is to avoid the following warning that clang generates:

template <int N>
struct Vector
{
    float v[N];
};

template <>
struct Vector<2>
{
    float x, y;
};

int main()
{
    Vector<2> v2 = {1.0f, 2.0f};         // Yay, works
    Vector<3> v3 = {1.0f, 2.0f, 3.0f};   // Results in warning in clang++
    Vector<3> u3 = {{1.0f, 2.0f, 3.0f}}; // Must use two braces to avoid warning
    // If I could make Vector<N> take an initializer list in its constructor, I
    // could forward that on to the member array and avoid using the double braces
}

warning: suggest braces around initialization of subobject

So my question is: How can I initialize a member array with an initializer list? (i.e. How can I make the first code work? Or is it not possible?

like image 755
Cornstalks Avatar asked May 02 '13 14:05

Cornstalks


2 Answers

Plain C style arrays are not assignable. If you switch over to using std::array instead, the initialization becomes trivial.

#include <array>

struct Vector
{
    std::array<float, 3> v;

    Vector(std::array<float, 3> const& values) 
    : v(values)
    {}
};

int main()
{
    Vector v{{1, 2, 3}};
}
like image 198
Praetorian Avatar answered Oct 16 '22 06:10

Praetorian


If your class is an aggregate, you can use your syntax

template < std::size_t len >
struct Vector
{
    float elems[len];
};

Vector<3> v = {1.0f, 2.0f, 3.0f};

Note: this is possible due to brace-elision. No need for v = {{1,2,3}}; though this is also possible. Clang issues this warning because the clearer and less error-prone syntax is with the double braces (one for initializing v and one for initializing the sub-aggregate elems).

If your class is not an aggregate, you can use variadic templates:

#include <array>
#include <cstddef>

template < std::size_t len >
struct Vector
{
    std::array < float, len > m;  // also works with raw array `float m[len];`

    template < typename... TT >
    Vector(TT... pp) : m{{pp...}}
    {}

    // better, using perfect forwarding:
    // template < typename... TT >
    // Vector(TT&&... pp) : m{{std::forward<TT>(pp)...}}
    // {}
};

Vector<3> a = {1.0f, 2.0f, 3.0f};
like image 28
dyp Avatar answered Oct 16 '22 06:10

dyp