Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Brace-enclosed initializer list constructor

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?

like image 285
bretttolbert Avatar asked Nov 07 '10 13:11

bretttolbert


People also ask

What is brace initialization?

If a class has non-default constructors, the order in which class members appear in the brace initializer is the order in which the corresponding parameters appear in the constructor, not the order in which the members are declared (as with class_a in the previous example).

What is a constructor initializer list?

Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.

What is constructor C++?

A constructor is a member function with the same name as its class. For example: class X { public: X(); // constructor for class X }; Constructors are used to create, and can initialize, objects of their class type.

What is uniform initialization in C++?

Uniform initialization is a feature in C++ 11 that allows the usage of a consistent syntax to initialize variables and objects ranging from primitive type to aggregates. In other words, it introduces brace-initialization that uses braces ({}) to enclose initializer values.


3 Answers

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 :)

like image 116
Johannes Schaub - litb Avatar answered Sep 23 '22 04:09

Johannes Schaub - litb


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 ... }; 
like image 29
Magnus Hoff Avatar answered Sep 22 '22 04:09

Magnus Hoff


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};
}
like image 42
evnu Avatar answered Sep 21 '22 04:09

evnu