2016-11-22 54 views
6

考慮下面的代碼片斷:擴展參數包入λ與倍的表達 - 的gcc VS鐺

template <typename TF> 
void post(TF){ } 

template <typename... TFs> 
struct funcs : TFs... 
{ 
    funcs(TFs... fs) : TFs{fs}... { } 

    void call() 
    { 
     (post([&]{ static_cast<TFs&>(*this)(); }), ...); 
    } 
}; 

clang++ 3.8+ successfully compiles the code

g++ 7.0 fails to compile,出現以下錯誤:

prog.cc: In lambda function: 
prog.cc:10:43: error: parameter packs not expanded with '...': 
     (post([&]{ static_cast<TFs&>(*this)(); }), ...); 
        ~~~~~~~~~~~~~~~~~~~~~~~~^~ 
prog.cc:10:43: note:   'TFs' 
prog.cc: In member function 'void funcs<TFs>::call()': 
prog.cc:10:13: error: operand of fold expression has no unexpanded parameter packs 
     (post([&]{ static_cast<TFs&>(*this)(); }), ...); 
     ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

卸下post調用和拉姆達makes g++ compile the fold expression

這是lambdas之間的相互作用,摺疊表達式和模板函數調用不知何故被標準禁止,或者這是一個海灣合作委員會的錯誤?

+4

海灣合作委員會有[長期問題](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226)與包擴展一個完整的lambda。不完全是新的。 –

+0

我會帶着門#3,蒙蒂:尚未完全實施。 –

回答

9

這是old gcc bug。這是gcc的模板處理比MSVC差的少數情況之一。慚愧gcc。恥辱。

有時可行的解決方法是使用標籤和打包擴展。

template<class T>struct tag_t{using type=T; constexpr tag_t(){};}; 
template<class T>constexpr tag_t<T> tag{}; 
template<class Tag>using type_t=typename Tag::type; 
#define TAG2TYPE(...) type_t<decltype(__VA_ARGS__)> 

// takes args... 
// returns a function object that takes a function object f 
// and invokes f, each time passing it one of the args... 
template<class...Args> 
auto expand(Args&&...args) { 
    return [&](auto&& f)->decltype(auto) { 
    using discard=int[]; 
    (void)discard{0,(void(
     f(std::forward<Args>(args)) 
    ),0)...}; 
    }; 
} 

template <typename TF> 
void post(TF){ } 

template <typename... TFs> 
struct funcs : TFs... 
{ 
    funcs(TFs... fs) : TFs{fs}... { } 

    void call() { 
    expand(tag<TFs>...) 
    ([&](auto tag){ 
     post(static_cast< TAG2TYPE(tag)& >(*this)()); 
    }); 
    } 
}; 

我們小心翼翼地避免在每次傳遞lambda的時候擴展到lambda結尾。相反,我們採用一組參數並將其擴展爲一組lambda調用。

lambda獲取作爲標籤傳入的類型,然後我們將其轉換回類型。

Live example

做的expand不存儲返回類型,如果你通過它臨時變量。

4

這是一個公知的克++蝽(#47226),該報道在2011年...