2015-08-12 10 views
10

Speziell Clang 3.6.0, die derzeit von Coliru gehostet wird.Clang und die binären falten Ausdrücke - Der Fluch des leeren Parameterpakets

int main() { 
    foo(); 
    std::cout << "\n----\n"; 
    foo(1, 2, 3); 
} 

Der folgende Code:

Alle diese Schnipsel aus genannt

template <class... Args> 
void foo(Args... args) { 
    std::cout << ... << args; 
} 

Löst die folgenden Kompilierungsfehler:

main.cpp:7:17: error: expected ';' after expression 
    std::cout << ... << args; 
       ^
       ; 
main.cpp:7:15: error: expected expression 
    std::cout << ... << args; 
      ^

Also habe ich versucht Klammern um den Ausdruck setzen :

(std::cout << ... << args); 

Es funktioniert, aber löst eine Warnung:

main.cpp:7:6: warning: expression result unused [-Wunused-value] 
    (std::cout << ... << args); 
    ^~~~~~~~~ 
main.cpp:11:5: note: in instantiation of function template specialization 'foo<>' requested here 
    foo(); 
    ^

Also habe ich versucht, den Wert des Ausdrucks mit einer Funktion-Casts zu void zu verwerfen:

void(std::cout << ... << args); 

Aber:

main.cpp:7:20: error: expected ')' 
    void(std::cout << ... << args); 
       ^
main.cpp:7:9: note: to match this '(' 
    void(std::cout << ... << args); 
     ^

Ich versuchte eine static_cast auch für das gleiche Ergebnis.

Also habe ich versucht, mit einem C-Druckguss statt:

(void)(std::cout << ... << args); 

Aber dann:

main.cpp:6:18: warning: unused parameter 'args' [-Wunused-parameter] 
void foo(Args... args) { 
       ^

... und meine Ausgabe ist nur : foo(1, 2, 3); nicht mehr ausgegeben tut!

Wird Clang von einer bösen Kraft aus zukünftigen Standards verflucht, hat es einen Fehler, oder sitzt das Problem gerade auf meinem Stuhl?

+0

Ich kann dies in keinem Compiler kompilieren. versuchte MSVC2015 und http://gcc.godbolt.org/ – NathanOliver

+2

'static_cast ((std :: cout << ... << args));' scheint zu arbeiten (das heißt, doppelte Parens), und meine Vermutung ist dieser Klang ist korrekt, da ein Faltexpression sein eigenes Paar von Parentesis erfordert –

+0

Ich hatte das gleiche Setup mit Cin. Wenn ich mich erinnere, ist das Problem, dass irgendwie versucht wird, '(cin << (1 << 2))' statt '((cin >> 1) >> 2)' – bolov

Antwort

9

Sie benötigen einen zusätzlichen Satz von Klammern beim Umwandeln in void mit der funktionalen Notation "cast", andernfalls werden die Klammern als Teil des Darstellungsausdrucks anstelle des Faltungsausdrucks betrachtet. Die fold expression syntax selbst benötigt einen Satz Klammern.

Alle folgenden Arbeiten ohne Warnungen zu produzieren:

void((std::cout << ... << args)); 
(void)((std::cout << ... << args)); 

Oder rufen Sie einfach einige ostream Memberfunktion die ungenutzte Ergebnis Warnung

(std::cout << ... << args).flush(); 

Als T. C. zu vermeiden Erwähnungen in den Kommentaren unten, das Verhalten mit (void)(std::cout << ... << args); scheint wie ein Clang Bug. Die Syntax für eine Cast-Notation ist in angegeben. 5.4 [expr.gegossen]

gossenen Ausdruck:
    unären-expression
    (Typ-ID) gossenen Ausdruck

Da Klammern nicht erforderlich sind, wie Teil des Cast-Ausdrucks sollte diese Verwendung keine Warnungen erzeugen, und was noch wichtiger ist, sollte es sein Ergebnis beim Drucken der Argumente.

+4

Ich sehe nicht warum '(void) (std :: cout << ... << args); 'funktioniert aber nicht. Dieser Teil sieht für mich wie ein Käfer aus. –

+6

Berichtet als https://llvm.org/bugs/show_bug.cgi?id=24440 –

+1

Also ist die Antwort "alle drei" dann. Vielen Dank, Praetorian für die Antwort und T.C. für den Fehlerbericht :) – Quentin

2

A fold Ausdruck, von [expr.prim.fold] ist:

Eine Falte Ausdruck führt eine Falte eines Templats Parametersatz (14.5.3) über einen binären Operator.
        fach-expression:
                (guss Ausdruckfach-Operator ...)
                (.. . Falte-Oper torguss Ausdruck)
                (guss Ausdruckfach-Operator ... fach-Operatorguss Ausdruck)

Hinweis In allen Fällen sind die Klammern Teil der Grammatik. So Ihr erstes Beispiel ist syntaktisch falsch, und sein muss:

template <class... Args> 
void foo(Args... args) { 
    (std::cout << ... << args); 
} 

die dann Sie im Falle eines leeren Packung eine Warnung geben, da die binäre fache reduziert sich auf nur std::cout; Um dieser Warnung loszuwerden, Sie gehen kann, den üblichen Weg des Gießens void - nur, dass der innere Satz Klammern Teil der Grammatik, so dass Sie zwei brauchen:

void((std::cout << ... << args)); 

oder Sie gerade in einem extra endl oder dergleichen werfen könnte:

(std::cout << ... << args) << std::endl; 

Oder senden Sie das Ergebnis:

template <class... Args> 
std::ostream& foo(Args... args) { 
    return (std::cout << ... << args); 
} 
2

Ich beschloss, einen besseren Blick auf diesen Fehler in der Clang Quelle zu übernehmen. Hier ist der anstößige Codeabschnitt.Dieser Fall tritt auf, wenn es fertig ist nur (<type>) Parsen und ist jetzt eine folgende klammerten Ausdrucksanalyse:

} else if (isTypeCast) { 
    // Parse the expression-list. 
    InMessageExpressionRAIIObject InMessage(*this, false); 

    ExprVector ArgExprs; 
    CommaLocsTy CommaLocs; 

    if (!ParseSimpleExpressionList(ArgExprs, CommaLocs)) { 
    // FIXME: If we ever support comma expressions as operands to 
    // fold-expressions, we'll need to allow multiple ArgExprs here. 
    if (ArgExprs.size() == 1 && isFoldOperator(Tok.getKind()) && 
     NextToken().is(tok::ellipsis)) 
    return ParseFoldExpression(Result, T); 

    ExprType = SimpleExpr; 
    Result = Actions.ActOnParenListExpr(OpenLoc, Tok.getLocation(), 
             ArgExprs); 
    } 
} 

// The beginning of ParseFoldExpression(LHS, T): 
if (LHS.isInvalid()) { 
    T.skipToEnd(); 
    return true; 
} 

Der spezifische Teil des Codes verantwortlich für diesen Fehler ist hier:

return ParseFoldExpression(Result, T); 

Es stellt sich heraus, dass Result wird niemals von seinem ursprünglichen true Wert getrennt gesetzt. Ich glaube, es sollte auf ArgExprs.front() gesetzt werden, das jetzt std::cout hält.

Jetzt werden Sie auch das FIXME bemerken. Obwohl es speziell mit diesem Thema nicht zu tun hat, könnte es sich lohnen, das Problem zu beheben.

Als mein erster Clang-Fix habe ich noch einige Dinge zu tun, bevor ich eine Änderung einreiche (als Referenz ist Clang 4.0 in der Entwicklung). Ich wäre mehr als glücklich darüber, dass das überhaupt funktioniert, egal ob von mir oder jemand anderem. Zumindest sind meine Ergebnisse für jetzt irgendwo dokumentiert.