2016-08-25 2 views
4

Я хочу передать не-захватывающий лямбда, который возвращает std::unique_ptr<Derived>, в качестве функции указателя типа std::unique_ptr<Base>(*)().Передача лямбда, которая возвращает polyorphic unique_ptr, в качестве указателя функции

Однако это работает только в том случае, если я явно указываю тип возврата лямбда как std::unique_ptr<Base>.

  • Почему я явно указываю тип возврата?
  • Почему это работает для std::function без дополнительного типа возврата?

#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 ошибка:

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) {} 

live example

+0

не будет 'std :: add_pointer_t >;' быть указателем на 'unique_ptr '? См. Http://coliru.stacked-crooked.com/a/5a1c461bfb6199a8 Возможно, я не эксперт, но могу угадать, что у компилятора есть проблема с преобразованием 'unique_ptr ' в указатель на 'unique_ptr ' при конвертировании a 'unique_ptr ' указателю на 'unique_ptr ' он может как-то делать неявно – Hayt

+0

@Hayt Я использую 'std :: add_pointer_t ()>;' обратите внимание на '()' в конце –

+0

Да, да. Никогда не думай об этом.Возможно, что-то нужно сделать с другим поведением функции ptr (которое является первым) и объектом функции с некоторым преобразованием, но я не мог найти никаких ссылок на это прямо сейчас (в комбинациях с lambdas. Возможно, что-то с конверсией от lambda до c-функции ptr). – Hayt

ответ

5

TL; DR;

  • FailsForF2 терпит неудачу, потому что std::unique_ptr<Derived> (*)() не неявно конвертируются в std::unique_ptr<Base> (*)();
  • Works работает, потому что std::unique_ptr<Derived> неявно конвертируется в std::unique_ptr<Base> (см. Стандартные кавычки в конце).

Лямбда неявно конвертируются в функции указатель с тем же типом возвращаемого значения и аргументы , так что ваши три лямбды соответственно конвертируются в:

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); 

... до тех пор, пока справедливо следующее :

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

Стандартная кодовая стоимость от N4594, § 5.1.5/7 (курсив мой):

Тип закрытия для не-родового лямбда-выражения, без лямбда-захвата имеет функцию преобразования в указатель на функцию с языковой связью C++ (7.5), имеющий тот же параметр и возвращаемые типы как оператор вызова функции замыкания . [...]

Стандартная цитата из N4594, § 20.12.12.2/2:

отзывной объект е типа F является Callable для типов аргументов ArgTypes и return type R, если выражение INVOKE (f, declval<ArgTypes>()..., R), рассматриваемое как неоцениваемый операнд (п. 5), хорошо сформировано (20.12.2).

... и § 20.12.2 (курсив мой, 1,1 через 1,6 около указателя (или так) к функциям-членам, поэтому не имеет значения здесь):

Определить INVOKE (f, t1, t2, ..., tN) следующим образом:

(1.x) - [...]

(1.7) - f(t1, t2, ..., tN) во всех остальных случаях.

Определить INVOKE (f, t1, t2, ..., tN, R), как static_cast (INVOKE (f, t1, t2, ..., tN)), если R является недействительным резюме, в противном случае INVOKE (f, t1, t2, ..., tN)неявно преобразуется в R.

1

В дополнение к ответу Холта и для рассмотрения вашего первого вопроса: Вам необязательно указывать тип возврата явно как возвращаемый возвращаемый тип. Но так как вы создаете unique_ptr<Derived>, но хотите unique_ptr<Base>, вы должны вернуть последнее и выполнить преобразование в своей функции.

Итак, я считаю, что это что-то вдоль линий

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

который также компилирует.

+1

Я предпочитаю 'make_unique' из-за его безопасности исключения –

+1

@ m.s. Здесь нет никакой проблемы с безопасностью (но я согласен с тем, что в общем вы должны пойти на 'make_unique'), но если вам нужно, вы можете' std :: unique_ptr (std :: make_unique ()) '(что в основном то, что 'f3' неявно или версия' std :: function'). Но если вам нужно преобразование в указатель функции, лучшим способом является явно указать тип возвращаемого значения, как в 'f3'. – Holt

Смежные вопросы