Question

I believe the example below in page 66 of Bjarne Stroutrup's new book, TCPL 4th edition, has a minor error, as the class Vector_container doesn't have an std::initializer_list constructor. The error message here confirms this.

#include <iostream>

class Vector{
    double* elem;
    int sz;
public:
    Vector(int s):elem{new double[s]}, sz{s} { for(int i = 0; i != sz; ++i) elem[i]= 0; }
    Vector(std::initializer_list<double> lst): elem{new double[lst.size()]}, sz(lst.size()) { std::copy(lst.begin(), lst.end(), elem); }
    ~Vector() { delete[] elem; }
    double& operator[](int i) { return elem[i]; }
    int size() const { return sz; }
};

class Container{
public:
    virtual double& operator[](int i) = 0;
    virtual int size() const = 0;
    virtual ~Container() {}
};

class Vector_container:public Container{
    Vector v;
public:
    Vector_container(int s): v{s}{}
    ~Vector_container() {}
    double& operator[](int i) { return v[i]; }
    int size() const {return v.size(); }
};

void use(Container& c)
{
    const int sz = c.size();
    for(int i = 0; i != sz; i++) std::cout << c[i] << '\n';
}

int main()
{
    Vector_container vc{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
    use(vc);
}

But the warning emitted for the expression v{s} in the member-initializer list for the Vector_container(int) constructor surprised me, as it says: warning: narrowing conversion of 's' from 'int' to 'double' inside { }, which doesn't seem to be correct, as there's no narrowing in this case.

Also, if you change the expression Vector_container vc{10, ..., 1}; in main() to Vector_container vc{10}; the error message disappears, as expected, but the warning continues to show off. Nonetheless the std::initializer-list constructor for the Vector class is chosen by the compiler, and I presume this to be correct, according to 13.3.1.7/1 in the Standard.

Thus, I wonder if there's any way to impose the invoking of the Vector(int) constructor, instead of the initializer-list ctor in the Vector class, in this last example with Vector_container vc{10};.

Was it helpful?

Solution

You're correct about the overload resolution: Inside

Vector_container(int s): v{s}{}

the initialization v{s} selects the following constructor:

Vector(std::initializer_list<double> lst)

as per [over.match.list]/1.

When the std::initializer_list<double> is created from {s}, where s is of type int, there is a narrowing conversion from int to double (n3485) [dcl.init.list]/7

A narrowing conversion is an implicit conversion

  • [...]
  • from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
  • [...]

Note that s here is not a constant expression any more (being a parameter). Narrowing conversions may not appear in the construction of an std::initializer_list object, [dcl.init.list]/5

If a narrowing conversion is required to initialize any of the elements, the program is ill-formed.

So that warning should be an error (or it's an extension).


Thus, I wonder if there's any way to impose the invoking of the Vector(int) constructor, instead of the initializer-list ctor in the Vector class.

I'm not sure if I understood you correctly (see comments to OP), but not using list-init here solves the problem:

Vector_container(int s): v(s) {}  // initializer-list ctor not viable
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top