2012-05-11 63 views
1

假設我有以下示例性功能:如何避免在decltype表達式中指定變量?

template <typename Fn> auto call(Fn fn) -> decltype(fn()) { 
    return fn(); 
} 

關於這個功能的重要一點是,它的返回類型取決於它的模板參數,從而可以推斷。所以最終,返回類型取決於函數的調用方式。

現在,我們也有一個測試類:

struct X { 
    int u; 
    auto test() -> decltype(call([this]() -> double {this->u = 5; return 7.4;})) { 
    return call([this]() -> double {this->u = 5; return 7.4;}); 
    } 
}; 

,你可以看到,X::test調用call,返回相同的返回值。在這種情況下,返回類型簡單地給出爲double,但讓我們假設一下,我們不知道call做什麼,並且lambda具有更復雜的返回類型。

如果我們嘗試編譯,編譯器會抱怨,因爲我們使用this在頂層(不是在一個範圍內,將允許一個表達式):

error: lambda-expression in unevaluated context 
error: invalid use of ‘this’ at top level 

不過,我使用捕獲的lambda,我通過call爲了得到call的返回類型的權利。你會如何解決這個問題,同時還要離開lambda?

注意:當然,我可以將lambda移動到某個幫助類型的operator(),我使用this指針的副本實例化,但我想避免該樣板。

+0

我希望至少符合gcc 4.5。 – bitmask

+0

你不能把lambda表達式放在例如'decltype'(這是第一個錯誤所指的),所以你也有另一個問題。 –

+0

@LucDanton:謝謝。我沒有意識到這一點。你會認爲這個問題的延伸?我應該打開一個新的?任何機會繞過它,無論如何? – bitmask

回答

2

我認爲真正需要關注的錯誤是「未評估上下文中的lambda表達式」。您無法在未評估的上下文中使用lambda,因爲每個lambda表達式都有唯一的類型。也就是說,如果允許decltype([]{}),它會在某些其他上下文中推斷出與[]{}不同的類型。即decltype([]{}) fn = []{};不起作用。

除非你想明確地寫出返回類型而不是推導出來,否則我不認爲你別無選擇,只能創建一個真正的類型,你可以在需要的上下文中使用它, 。

雖然如果改變test到不是一個成員函數是可以接受的,那麼你可以使用一個事實,即拉姆達的可以忽略它,如果身體只有一個return語句推斷出他們的返回類型:

template <typename Fn> auto call(Fn fn) -> decltype(fn()) { 
    return fn(); 
} 

struct X { 
    int u; 
}; 

int main() { 
    auto test = [](X *x) { return call([x]() -> double {x->u = 5; return 7.4; });}; 

    X x; 
    test(&x); 
} 

它如果函數的結尾返回類型語法具有相同的屬性,那將會很好。我不確定它爲什麼沒有。

0

這似乎是一個由(解釋,人工的)問題,因爲

  • 如果你從別的地方獲得的λ,那麼它的命名並沒有什麼問題結合this

  • 如果你沒有從其他地方得到lambda,那麼你知道結果類型。

總之,這個問題目前說(我在寫這個答案)有一個,除了通過自己的意志強加沒有問題。

但是,如果你堅持這一點,那麼只需通過this作爲參數,而不是通過lambda定義來綁定它。然後撥打call,綁定參數。但是,也許不用說,因爲這隻能解決一個構成問題,這是一個真正的Rube Goldberg構造,體面的過度開花不必要的複雜性,不能解決任何真正的複雜問題。

什麼是原真正的問題,如果有的話?

+0

問題沒有解決。也許我沒有看到你明顯的解決方案(這不是諷刺,我經常忽視這個明顯的問題)。不考慮'call'的定義什麼是'X :: test'的返回類型? – bitmask

+0

@bitmask:你需要知道'call'的def才能使用它。你需要知道你的lambda爲了使用它。問題解決了,這種情況下的返回類型是'double'。如果你真的必須使用一個未知的'call',只需將一個虛擬lambda(與真實的''具有相同的返回類型)傳遞給'decltype'中的調用。再次,問題解決了,因爲*你在呼叫站點*擁有所有必要的知識。 –

+0

對不起,如果Luc在他對主要問題的評論中是正確的,那麼就不能像你那樣在'decltype'中放入一個lambda。我不知道。但檢查出來! –

0

您不應該始終將功能體複製並粘貼到decltype。引入遲後指定返回類型的要點是,您將能夠以某種方式從參數中推斷正確的返回類型。
例如auto f(T x) -> decltype(g(x)) { return h(), g(x); },不-> decltype(h(), g(x))

所以,你的情況,double test()是不夠的,因爲我們知道call行爲,我們知道我們返回傳遞給它的lambda表達式的類型。
在更復雜的情況下,我們應該通過使用關於call和其他東西的知識來減少decltype中的代碼。