Brace-enclosed initializer list constructor

87,970

Solution 1

It can only be done for aggregates (arrays and certain classes. Contrary to popular belief, this works for many nonpods too). Writing a constructor that takes them is not possible.

Since you tagged it as "C++0x", then this is possible though. The magic words is "initializer-list constructor". This goes like

Phenotype(std::initializer_list<uint8> c) {
  assert(c.size() <= std::size(m_array));
  std::copy(c.begin(), c.end(), m_array);
}

// used like
Phenotype p1{1, 2, 3};
Phenotype p2({1, 3, 2}); // works too
Phenotype p3(1, 2, 3); // doesn't work

However, such initialization will default construct the array and then use the assignment operator. If you aim for speed and safety (you get compile time errors for too many initializers!), you can also use an ordinary constructor with a variadic template.

This can be more generic than needed though (often an initializer_list completely suffices, especially for plain integers). It benefits from perfect forwarding, so that an rvalue argument can be move constructed into an array element

template<typename ...T>
Phenotype(T&&...t):m_array{ std::forward<T>(t)... } {

}

// used like
Phenotype p1{1, 2, 3}; 
Phenotype p2(1, 2, 3); // works too
Phenotype p3({1, 2, 3}); // doesn't work   

It's a hard choice!

Edit Correction, the last one works too, as we didn't make the constructor explicit, so it can use the copy constructor of Phenotype, constructing a temporary Phenotype object and copy it over to p3. But that's not what we really would want the calls to be :)

Solution 2

In C++0x it seems you can create a constructor for this. I have no experience with it myself, but it looks like it's called initializer list-constructor.

A container might implement an initializer-list constructor like this:

template<class E> class vector {
public:
    vector (std::initializer_list<E> s) // initializer-list constructor
    {
        reserve(s.size());  // get the right amount of space
        uninitialized_copy(s.begin(), s.end(), elem);   // initialize elements (in elem[0:s.size()))
        sz = s.size();  // set vector size
    }

    // ... as before ...
};

Solution 3

You need to use the std::initializer_list template type. Example:

#include <iostream>
class X {
    public:
        X (std::initializer_list<int> list) {
        for (auto i = list.begin(); i != list.end(); i++) {
                std::cout << *i << std::endl;
            }
        }
};


int main () {
    X x = {1,2,3,4,5};
}
Share:
87,970

Related videos on Youtube

bretttolbert
Author by

bretttolbert

Updated on October 02, 2020

Comments

  • bretttolbert
    bretttolbert over 3 years

    I have class Phenotype with the following constructor:

    Phenotype(uint8 init[NUM_ITEMS]);
    

    I can create a Phenotype like this:

    uint8 data[] = {0,0,0,0,0};
    Phenotype p(data);
    

    But I get an error when I try to create one like this:

    Phenotype p = {0,0,0,0,0};
    

    Output:

    $ make
    g++ -Wall -g main.cpp -std=c++0x
    main.cpp: In function ‘int main(int, char**)’:
    main.cpp:109: error: no matching function for call to ‘Phenotype::Phenotype(<brace-enclosed initializer list>)’
    main.cpp:37: note: candidates are: Phenotype::Phenotype(uint8*)
    

    The error seems to indicate that there is a way to define a constructor which takes a brace-enclosed initializer list. Does anyone know how this might be done?

  • Paul Renton
    Paul Renton over 10 years
    Very helpful. Been looking around for answer to this for a while
  • Don Larynx
    Don Larynx about 9 years
    He tagged it as c++11. But how come it doesn't work in c++11?
  • Danilo
    Danilo over 4 years
    @Johanes does it need additional deconstructor for such initialization ?
  • darkdragon
    darkdragon over 3 years
    Is there a way to specify that T must not be Phenotype? This replaces my default move constructor...