2016-11-22 8 views
6

Biorąc pod uwagę kod poniższym przykładzie:Rozszerzanie parametru pakiet do lambda ze składaną ekspresji - GCC vs brzękiem

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 z powodu następującego błędu:

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)(); }), ...); 
     ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

Demontaż połączenia post i lambda makes g++ compile the fold expression.

Czy ta interakcja między lambdami, wyrażeniami zagięcia i wywołaniami funkcji szablonów jest w jakiś sposób zabroniona przez standard, czy jest to błąd gcc?

+4

GCC ma [długotrwałe problemy] (https://gc.gnu.org/bugzilla/show_bug.cgi?id=47226) z pakietem rozszerzającym całą lambdę. Niezupełnie nowy. –

+0

Ja wezmę drzwi # 3, Monty: jeszcze nie w pełni zaimplementowane. –

Odpowiedz

9

To old gcc bug. Jest to jeden z niewielu przypadków, w których obsługa szablonów gcc jest gorsza niż MSVC. Wstyd gcc. Wstyd.

Obejście, które czasami działa, polega na używaniu tagów i rozszerzania pakietów.

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

w którym ostrożnie unikamy rozszerzania się pod koniec lambda poprzez podanie lambda za każdym razem. Zamiast tego przyjmujemy zestaw argumentów i rozszerzamy go do zestawu wywołań lambda.

W lambda dostaje się typy przekazane jako tag, a następnie przekonwertujemy je z powrotem na typ.

Live example

nie przechowywać typ zwracanej expand jeśli przeszedł on tymczasowych.

4

Jest to znany bug g ++ (#47226), która została opisana w ... 2011.

Powiązane problemy