Pass lambda that returns polyorphic unique_ptr as a function pointer

I want to pass a non-capturing lambda that returns std::unique_ptr<Derived>as a pointer to a type function std::unique_ptr<Base>(*)().

However, this only works if I explicitly specify the return type of lambda as std::unique_ptr<Base>.

  • Why am I explicitly specifying a return type?
  • Why does this work for std::functionwithout this extra return type?

#include <functional>
#include <memory>

struct Base{virtual ~Base()=default;};
struct Derived : Base{};

struct FailsForF2
{
   using Function = std::add_pointer_t<std::unique_ptr<Base>()>;
   FailsForF2(Function f) {}
};

struct Works
{
   using Function = std::function<std::unique_ptr<Base>()>;
   Works(Function f) {}
};


std::unique_ptr<Derived> fun() {return std::make_unique<Derived>();} 

int main()
{

   auto f1 = [](){return std::make_unique<Base>();};
   auto f2 = [](){return std::make_unique<Derived>();};
   auto f3 = []()->std::unique_ptr<Base>{return std::make_unique<Derived>();};

   Works x1(f1);
   Works x2(f2);
   Works x3(f3);

   FailsForF2 x4(f1);
   FailsForF2 x5(f2);
   FailsForF2 x6(f3);
}

Gcc error :

main.cpp: In function 'int main()':

main.cpp:34:20: error: invalid user-defined conversion from 'main()::<lambda()>' to 'FailsForF2::Function {aka std::unique_ptr<Base> (*)()}' [-fpermissive]

    FailsForF2 x5(f2);

                    ^

main.cpp:26:17: note: candidate is: main()::<lambda()>::operator std::_MakeUniq<Derived>::__single_object (*)()() const <near match>

    auto f2 = [](){return std::make_unique<Derived>();};

                 ^

main.cpp:26:17: note:   no known conversion from 'std::_MakeUniq<Derived>::__single_object (*)() {aka std::unique_ptr<Derived> (*)()}' to 'FailsForF2::Function {aka std::unique_ptr<Base> (*)()}'

main.cpp:10:4: note:   initializing argument 1 of 'FailsForF2::FailsForF2(FailsForF2::Function)'

    FailsForF2(Function f) {}

living example

+4
source share
2 answers

TL DR;

  • FailsForF2fails because it is std::unique_ptr<Derived> (*) ()implicitly converted to std::unique_ptr<Base> (*) ();
  • Works , std::unique_ptr<Derived> std::unique_ptr<Base> (. ).

1 :

std::unique_ptr<Base> (*) ()
std::unique_ptr<Derived> (*) ()
std::unique_ptr<Base> (*) ()

std::unique_ptr<Derived> (*) () ( ) std::unique_ptr<Base> (*) (), FailsForF2. . :

std::unique_ptr<Derived> (*pfd) () = f2; // Compiles.
std::unique_ptr<Base> (*pfb) () = pfd;   // Does not compile (invalid conversion).

, ( , . ), .


std::function, , . std::function , :

template <typename F>
std::function(F &&f);

... : 2:

INVOKE(f, std::forward<Args>(args)..., R)

1 N4594, 5.1.5/7 ( ):

- - - ++ (7.5), . [...] >

2 N4594, & sect; 20.12.12.2/2:

f F ArgTypes R, INVOKE (f, declval<ArgTypes>()..., R), ( 5), (20.12.2). >

... & sect; 20.12.2 ( , 1.1 1.6 ( ) -, ):

1 INVOKE (f, t1, t2, ..., tN) :

(1.x) - [...]

(1.7) - f(t1, t2, ..., tN) .

2 INVOKE (f, t1, t2, ..., tN, R) static_cast (INVOKE (f, t1, t2, ..., tN)), R cv void, INVOKE (f, t1, t2, ..., tN) R.

+5

: . unique_ptr<Derived>, unique_ptr<Base>, .

, , -

auto f2 = [](){ return std::unique_ptr<Base>{new Derived()}; };

.

+1

Source: https://habr.com/ru/post/1652478/


All Articles