2016-04-22 16 views
2

我有我的代碼庫中的各種功能,它需要一個通用的可調用對象,並在調用它之前將它傳遞給一系列嵌套的lambdas。例如:在嵌套lambdas中捕獲泛型可調用對象 - 總是轉發?

template <typename TF> 
void interface(TF&& f) 
{ 
    nested0([/*...*/]() 
     { 
      nested1([/*...*/](auto& x) 
       { 
        nested2([&x, /*...*/]() 
         { 
          f(x); 
         }); 
       }); 
     }); 
} 

注意interface由轉發參考(以前稱爲通用參考)服用TF類型的一個可調用對象。可調用對象通常是一個帶有各種捕獲變量的lambda表達式,既有值,也有引用。

在保持正確性的同時捕獲嵌套lambda中的f的最佳方式(就性能而言)是什麼?

我能想到的三個選項:

  1. 捕獲f的副本。

    nested0([f]() 
    { 
        nested1([f](auto& x) 
         { 
          nested2([&x, f]() 
           { 
            f(x); 
           }); 
         }); 
    }); 
    

    可能會導致不必要的副本,如果拍攝對象物mutable它可能會導致不正確的行爲。

  2. 捕獲f作爲參考。

    nested0([&f]() 
    { 
        nested1([&f](auto& x) 
         { 
          nested2([&x, &f]() 
           { 
            f(x); 
           }); 
         }); 
    }); 
    

    似乎是合理的,但可能會造成問題,如果任何嵌套的lambda表達式執行會超越的f所有者的動作。想象一下,如果nested2的身體在一個單獨的線程中執行 - f可能已經超出範圍。

  3. 使lambdas mutable和捕捉完美轉發。

    #define FWD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__) 
    
    nested0([f = FWD(f)]() mutable 
    { 
        nested1([f = FWD(f)](auto& x) mutable 
         { 
          nested2([&x, f = FWD(f)]() mutable 
           { 
            f(x); 
           }); 
         }); 
    }); 
    

    的lambda表達式必須mutable,因爲我們可能從拉姆達到另一個移動f。這種方法似乎可以避免不必要的副本,並且如果它需要超過原始調用者,則可以正確地移動可調用對象。

選項3總是最好的,還是它有任何潛在的缺點? ...或者根本沒有「最佳和正確」的方式(需要關於可調用對象的知識)

+0

爲什麼在內部lambda表達式中使用forward時,'f'不再引用函數參數? –

+0

@PiotrSkotnicki:因爲我仍然需要從「當前」lambda中捕獲外部lambda的'f'。如果'interface'的'f'被移動到第一個lambda中,我想將第一個lambda的'f'移到第二個中,等等。 –

+0

我的意思是,這相當於在內部lambda表達式中移動 –

回答

1

正如在評論中提到的那樣,對於這個問題給出的如此糟糕的背景很難說。
這就是說,最好的解決辦法我看來參考捕捉一切,打破了這個邏輯,只要你需要複製其目的是要活得比的f壽命,作爲一個例子:

nested0([&f]() { 
    n1([&f](auto& x) { 
     n2([&x, 
       // get a local copy of f 
       f{std::forward<TF>(f)}]() { 
      f(x); 
     }); 
    }); 
}); 

無論如何,對於這樣的問題沒有經驗法則。
最佳解決方案與實際問題很可能緊密結合。
像往常一樣。很公平。