initializing from empty std::initializer_list vs default constructor

130 Views Asked by At

I've got a class that basically contains a std::vector.
I can default-construct an object of this class, leaving the contained std::vector empty.
I can also value-construct it from a set of values, given through a braced-initializers list.
I'd like to have a consistent behavior between both forms,per se, passing an empty list should give an empty std::vector.
Yet I cannot reproduce the expected behavior, as the empty list actually construct a single default element list:

#include <iostream>
#include <vector>

struct Wrap {
    std::vector<double> v;
    Wrap() = default;
    Wrap(std::initializer_list<double> const &l) : v(l) {}
};

int main() {
    Wrap v0 = {};
    std::cout << v0.v.size() << "; expected 0\n";
    Wrap v1 = {{}};
    // expecting 0, getting 1
    std::cout << v1.v.size() << "; expected 0\n";
}

Live

From Why does double empty curly braces { { } } create a std::initializer_list with one element, not zero? I get that it is the expected behaviour of std::initializer_list so how can I redesign my class in order to have:

  • default construction giving empty vector;
  • braced-initializers list of value giving also an empty vector if list is empty?

NB I thought about template construction from T const (&)[N] yet I was interested in taking benefit from no-narrowing conversion enforced by std::initializer_list.

[EDIT] here is an extended example that shows how brace initialization rules confuse me (and possibily my libraries client)

#include <iostream>
#include <vector>

struct Wrap {
    std::vector<double> v;
    Wrap() { std::cout << "default constructor\n"; }
    Wrap(std::initializer_list<double> const &l) : v(l) {
        std::cout << "IL constructor\n";
    }
};

int main() {
    {
        Wrap v = {1, 2};
        std::cout << v.v.size() << "; expected 2\n";
    }
    {
        Wrap v = {{1, 2}};
        std::cout << v.v.size() << "; expected 2, braces elision?\n";
    }
    {
        Wrap v = {1};
        std::cout << v.v.size() << "; expected 1\n";
    }
    {
        Wrap v = {{1}};
        std::cout << v.v.size() << "; expected 1, braces elision?\n";
    }
    {
        Wrap v = {};
        std::cout << v.v.size()
                  << "; expected 0, default ctor has precedence other IL one\n";
    }
    {
        Wrap v = {{}};
        std::cout << v.v.size() << "; expected 0, braces elision?\n";
    }
}

And the obtained output:

Program returned: 0
IL constructor
2; expected 2
IL constructor
2; expected 2, braces elision?
IL constructor
1; expected 1
IL constructor
1; expected 1, braces elision?
default constructor
0; expected 0, default ctor has precedence other IL one
IL constructor
1; expected 0, braces elision?

Live

0

There are 0 best solutions below