2016-08-25 58 views
4

我想通過一個非捕獲lambda,返回一個 std::unique_ptr<Derived>,作爲類型爲std::unique_ptr<Base>(*)()的函數指針。將返回多態unique_ptr的lambda作爲函數指針傳遞

但是,如果我明確規定的拉姆達的返回類型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 '的指針時出現問題一個'unique_ptr '指向'unique_ptr '它可以以某種方式隱式地執行 – Hayt

+0

@Hayt我使用'std :: add_pointer_t ()>;'請注意'()'在末尾 –

+0

啊,對啊。那麼不要那麼着急。它可能必須做一些功能ptr(第一個)的不同行爲和一些轉換的函數對象,但我現在找不到任何的鏈接(與lambda一起組合)。可能會有一些轉換一個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>(見末標準引號)。

一個lambda隱式轉換爲具有相同的返回類型和參數一個函數指針,所以你的三個lambda表達式分別轉換爲:

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)作爲 閉包類型的函數調用操作符的參數和返回類型相同。 [...]

2從N4594標準報價,§ 20.12.12.2/2:

一個可調用對象F型的F是可贖回爲參數類型ArgTypes和如果表達式INVOKE (f, declval<ArgTypes>()..., R)被認爲是一個未評估操作數(第5章),則返回類型R的格式良好(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爲CV空隙,否則INVOKE (f, t1, t2, ..., tN)隱式轉換至R

1

除了Holt的回答,並涵蓋你的第一個問題:你不一定必須明確指定返回類型作爲結尾返回類型。但是由於您創建的是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

相關問題