2017-02-04 3 views
4

Betrachten Sie den folgenden Code, inspiriert von Barry Antwort auf this Frage:keine passende Funktion std :: Vorwärts mit lambda

// Include 
#include <tuple> 
#include <utility> 
#include <iostream> 
#include <type_traits> 

// Generic overload rank 
template <std::size_t N> 
struct overload_rank 
: overload_rank<N - 1> 
{ 
}; 

// Default overload rank 
template <> 
struct overload_rank<0> 
{ 
}; 

// Prepend argument to function 
template <std::size_t N, class F> 
auto prepend_overload_rank(F&& f) { 
    using rank = overload_rank<N>; 
    return [f = std::forward<F>(f)](rank, auto&&... args) -> decltype(auto) { 
     return std::forward<F>(f)(std::forward<decltype(args)>(args)...); // here 
    }; 
} 

// Main 
int main(int argc, char* argv[]) 
{ 
    auto f = [](int i){return i;}; 
    prepend_overload_rank<5>(f)(overload_rank<5>(), 1); 
    return 0; 
} 

Es ist nicht wegen der Linie Kompilieren bemerkt here, und ich verstehe nicht, warum:

With g++: 
error: no matching function for call to 'forward<main(int, char**)::<lambda(int)>&>(const main(int, char**)::<lambda(int)>&)' 
With clang: 
error: no matching function for call to 'forward' 

Ersetzen

return std::forward<F>(f)(std::forward<decltype(args)>(args)...); 

von

return f(std::forward<decltype(args)>(args)...); 

scheinbar macht es funktioniert, aber wieder verstehe ich nicht warum, und mein Ziel ist es, eine perfekte Weiterleitung der Funktion zu erreichen.

+2

Hinweis: das Hinzufügen von 'mutable' behebt es (' return [f = std :: vorwärts (f)] (Rang, Auto && ... args) veränderbar -> decltype (auto) {'). 'std :: forward' ist ein Ablenkungsmanöver; Das ist einfach eine Frage der Korrektheit. – ildjarn

+0

'decltype (auto)' ist falsch. Du brauchst SFINAE, um anzugreifen. – Barry

Antwort

2

Offenbar Compiler sind entweder abgehört oder dürfen Variablen durch Kopieren als const erfasst deklarieren, wenn der mutable Spezifizierer nicht vorhanden ist.
Ironischerweise erstellt die folgende mit GCC, aber es funktioniert nicht mit Klirren:

#include <type_traits> 

int main(int argc, char* argv[]) { 
    int i = 0; 
    [j = i](){ static_assert(std::is_same<decltype(j), const int>::value, "!"); }(); 
} 

in beiden Fällen um das Problem zu umgehen, dies zu tun:

return [f = std::forward<F>(f)](auto&&... args) -> decltype(auto) { 
    return std::forward<decltype(f)>(f)(std::forward<decltype(args)>(args)...); // here 
}; 

Das heißt, Sie das Schlüsselwort mutable weglassen, aber Sie müssen den tatsächlichen Typ der Kopie von f innerhalb des Lambda verwenden. Beachten Sie, dass die Originalf eine nicht-const Referenz zu einer Lambda-Funktion ist, also F kann von der Art decltype(f) innerhalb der Lambda abweichen.
Dies gilt in jedem Fall, auch für mutable Lambda. Als Beispiel:

#include <type_traits> 
#include<utility> 

struct S {}; 

template<typename T> 
void f(T &&t) { 
    [t = std::forward<T>(t)]()mutable{ static_assert(std::is_same<decltype(t), S>::value, "!"); }(); 
    // the following doesn't compile for T is S& that isn't the type of t within the lambda 
    //[t = std::forward<T>(t)]()mutable{ static_assert(std::is_same<decltype(t), T>::value, "!"); }(); 
} 

int main() { 
    S s; 
    f(s); 
} 

Im Allgemeinen sollten Sie die tatsächliche Art der erfassten Variable anstelle des in einem umgebenden Kontext bestimmten Typs verwenden.
Im konkreten Fall, auch wenn der Compiler fälschlicherweise das aufgenommene Variable als const erklärt, können Sie es ohne die mutable Spezifizierer, solange die Funktion Betreiber von f ist const Arbeit machen (das ist Ihr Fall für f ist main isn‘ t mutable als auch).

Eine weitere Möglichkeit, Ihre Schnipsel arbeiten zu lassen dies ist (wie in den Kommentaren zu der Frage vorgeschlagen):

return [f = std::forward<F>(f)](auto&&... args) mutable -> decltype(auto) { 
    return std::forward<F>(f)(std::forward<decltype(args)>(args)...); // here 
}; 

In diesem Fall kopieren erfassten Variablen kann nicht const und der Typ zu sein, gezwungen werden, die erwartet man.
Wie auch immer, ich würde vorschlagen, den obigen Ratschlag auch dann zu befolgen, wenn Sie vorhaben, den Spezifizierer mutable zu verwenden.


Hinweis.
Wie in this question diskutiert, entstand das Problem wegen eines Fehlers von GCC. Der Vorschlag, decltype(f) zu verwenden, steht noch. Es behandelt auch andere Arten von Problemen und arbeitet für den speziellen Fall. Wenn der Fehler behoben ist, wird der Code weiterhin ordnungsgemäß funktionieren.

+1

Vielleicht ist GCC fehlerhaft, aber dieser Bug hat nichts mit OPs Problem zu tun. –

+0

@ T.C. _GCC ist bugged_ ist nur ein Teil der Antwort. Ich hoffe, der Rest kann helfen. Irgendein Kommentar dazu? – skypjack

Verwandte Themen