Ghita
Ghita

Reputation: 4505

Issue with object initialization from initializer list

I have the following code:

    class A {
        public: 
        A(std::vector<std::shared_ptr<int>>){}
    };

    auto x = std::make_shared<int>(0);
    auto y = std::make_shared<int>(1);

    auto list = {x, y};
    auto res = std::make_shared<A>({x, y});

In the example if I pass to res variable list it compiles, otherwise as in the case of using initializer list directly it fails http://ideone.com/8jYsDY

I guess it has to do with the way type deduction works when initializer_list are involved. If this is standard conformant some reference would be good.

Upvotes: 1

Views: 184

Answers (1)

Praetorian
Praetorian

Reputation: 109089

std::make_shared deduces its second template parameter from the arguments to the function call. A braced-init-list is not an expression, and as such, has no type. Hence template argument deduction cannot deduce a type from it.

From §14.8.2.5/5 [temp.deduct.type]

The non-deduced contexts are:
  — ...
  — A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list type. [ Example:

 template<class T> void g(T);
 g({1,2,3}); // error: no argument deduced for T

—end example ]

auto, however, is a special case that is allowed to deduce std::initializer_list<T> from a braced-init-list.

§7.1.6.4/7 [dcl.spec.auto]

... Otherwise, obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list, with std::initializer_list<U>. Deduce a value for U using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer is the corresponding argument. If the deduction fails, the declaration is ill-formed. Otherwise, the type deduced for the variable or return type is obtained by substituting the deduced U into P. [ Example:

 auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
 auto x2 = { 1, 2.0 }; // error: cannot deduce element type

—end example ]

In your example, the variable list has the type initializer_list<shared_ptr<int>>, and when you pass it to make_shared, a vector can be constructed from it, which is then used to direct-initialize the A instance.

Other options are to construct a vector

auto res = std::make_shared<A>(std::vector<std::shared_ptr<int>>{x, y});

construct an A, which will then be moved

auto res = std::make_shared<A>(A{{x, y}});

or specify the template parameters for make_shared explicitly

auto res = std::make_shared<A, std::initializer_list<std::shared_ptr<int>>>({x, y});

Upvotes: 1

Related Questions