简体   繁体   中英

std::initializer_list not able to be deduced from <brace-enclosed initializer list>

I have a class whose constructor takes an initializer_list :

Foo::Foo(std::initializer_list<Bar*> bars)

If I attempt to create an object with a brace-enclosed initializer list directly , the initializer_list is correctly deduced:

Foo f ({ &b }); // std::initializer_list<Bar*> correctly deduced

However, when trying to do the same indirectly (with a variadic function template - in this case make_unique ), the compiler is unable to deduce the initializer_list :

std::make_unique<Foo>({ &b }); // std::initializer_list<Bar*> not deduced

Error output:

error: no matching function for call to 'make_unique(<brace-enclosed initializer list>)'

Questions:

  • Why is the compiler failing to deduce { &b } as a initializer_list<Boo*> ?
  • Is it possible to use the syntax std::make_unique<Foo>({ &b }) which I desire?

Full example below:

#include <initializer_list>
#include <memory>

struct Bar
{};

struct Foo
{
    Foo(std::initializer_list<Bar*> bars)
    { }
};

int main()
{
    Bar b;

    // initializer_list able to be deduced from { &b }
    Foo f ({ &b });

    // initializer_list not able to be deduced from { &b }
    std::unique_ptr<Foo> p = std::make_unique<Foo>({ &b });

    (void)f;
    return 0;
}

A braced initializer has no type. When you call make_unique it tries to deduce the type and fails. In this case you have to specify the type when calling like

std::make_unique<Foo>(std::initializer_list<Bar*>{ &b });

This will create a std::initializer_list<Bar*> which the compiler can deduce and it will forward it to Foo::Foo(std::initializer_list<Bar*> bars)

The reason Foo f ({ &b }); works is that the compiler knows of the constructor Foo(std::initializer_list<Bar*> bars) and braced initializer list of B* s can be implicitly converted to a std::initializer_list<Bar*> . There is not type deduction going on.

make_unique uses perfect forwarding.

Perfect forwarding is imperfect in the following ways:

  • It fails to forward initializer lists

  • It converts NULL or 0 to an integer, which can then not be passed to a value of pointer type.

  • It does not know what type its arguments will be, so you cannot do operations that require knowing their type. As an example:

     struct Foo { int x; }; void some_funcion( Foo, Foo ) {}; template<class...Args> decltype(auto) forwarding( Args&& ... args ) { return some_function(std::forward<Args>(args)...); } 

    Calling some_function( {1}, {2} ) is legal. It constructs the Foo s with {1} and {2} .

    Calling forwarding( {1}, {2} ) is not. It does not know at the time you call forwarding that the arguments will be Foo s, so it cannot construct it, and it cannot pass the construction-initializer-list through the code (as construction-lists are not variables or expressions).

  • If you pass an overloaded function name, which overload cannot be worked out at the point of call. And a set of overloads is not a value, so you cannot perfect forward it through.

  • You cannot pass bitfields through.

  • It forces takes a reference to its arguments, even if the forwarded target does not. This "uses" some static const data in ways that can cause a program to be technically ill-formed.

  • A reference to an array of unknown size T(&)[] cannot be forwarded. You can call a function taking a T* with it, however.

About half of these were taken from this comp.std.c++ thread , which I looked for once I remembered there were other issues I couldn't recall off the top of my head.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM