2015-01-28 8 views
7

Ist dieses Codebeispiel gültig?Ist die zurückgegebene einheitliche initialisierte Referenz gültig?

using ref = char&; 

ref foo(ref x) { 
    return ref{x}; 
} 

int main() { 
    char a; 
    foo(a); 
    return 0; 
} 

scheint, dass:

  • Klirren 3.5 sagt JA
  • gcc 4.9 sagt NEIN

    main.cpp: In function 'char& foo(ref)': 
    main.cpp:4:15: error: invalid cast of an rvalue expression of type 'char' to type 'ref {aka char&}' 
        return ref{x}; 
          ^
    

http://coliru.stacked-crooked.com/a/cb6604b81083393f

Also welcher Compiler ist richtig? oder ist es nicht spezifiziert?

Es ist sehr einfach so von gcc Build-Fehler überwinden:

  1. Klammern anstelle von Klammern

    ref foo(ref x) { 
        return ref(x); 
    } 
    
  2. durch die Benennung zurückgegebene Wert

    ref foo(ref x) { 
        ref ret{x}; 
        return ret; 
    } 
    

Option 1. unterbricht die einheitliche Initialisierung, Option 2 fügt eine nutzlose Codezeile hinzu.

ähnliche Frage wurde bereits hier aked: Why can't I initialize a reference in an initializer list with uniform initialization?

Aber erwähnt pr50025 4.9 in gcc fixiert ist.

Ich weiß, dass oben Codebeispiel ist ziemlich nutzlos, , aber ich übermäßig vereinfacht es absichtlich auf das Problem hinweisen. Im wirklichen Leben Code Problem kann in einer generischen Funktion wie ausgeblendet werden:

#include <utility> 
template <typename Tp, typename... Us> 
Tp bar(Us&&... us) { 
    return Tp{std::forward<Us>(us)...}; 
} 

Antwort

3

Dies scheint wie eine Auslassung in der Norm, wo GCC setzt genau das, was die Norm verlangt, und Klirren wird für das, was wahrscheinlich gedacht.

aus C++ 11 (Hervorhebung von mir):

5.2.3 Explicit Typkonvertierung (funktionale Notation) [expr.type.conv]

1 A simple-Typ- Spezifizierer (7.1.6.2) oder typename-specifier (14.6) gefolgt von einem geklammerten expression-list konstruiert einen Wert des angegebenen Typs in der Ausdruck Liste. Wenn die Ausdrucksliste ein einzelner Ausdruck ist, ist der Typkonvertierungsausdruck dem definierten Ausdruck (5.4) äquivalent (definiert und wenn definiert). [...]

[...]

]

3 In ähnlicher Weise ein einfachen -Typ-Spezifizierer oder typename-Spezifizierer gefolgt von einer verspannt-init-Liste erzeugt ein temporäres Objekt des genannten Typs Direkt Liste initialisiert (8.5.4) mit der angegebenen braced-init-Liste, und sein Wert ist das temporäre Objekt als prvalue.

Für den verspannt-init-Liste Fall des Standard spezifiziert nicht, dass dies wie ein C-Casts arbeitet. Und es funktioniert nicht:

typedef char *cp; 
int main() { 
    int i; 
    (cp(&i)); // okay: C-style casts can be used as reinterpret_cast 
    (cp{&i}); // error: no implicit conversion from int * to char * 
} 

Leider T(expr) äquivalent zu (T)expr ist auch die einzige Ausnahme, bei dem eine funktionelle Guss nicht notwendigerweise eine prvalue produzieren. Der Standard kann eine ähnliche Ausnahme für einen Funktions-Cast nicht angeben, der eine stained-init-Liste für einen Referenztyp verwendet. In diesem Beispiel erstellt ref{x} einen temporären Typ ref, der von {x} direkt initialisiert wird. Dieses Temporary wird dann als prvalue behandelt, da dies der Standard für das Verhalten ist, und dass prvalue nicht für die Bindung an eine Lvalue-Referenz verwendet werden kann.

Ich vermute stark, dass, wenn dies zum ISO C++ - Komitee gebracht würde, der Standard geändert werden würde, um das Verhalten von Clang zu verlangen, aber basierend auf der aktuellen Formulierung des Standards, denke ich, dass GCC zumindest für Sie korrekt ist spezifisches Beispiel.

Anstatt eine Variable hinzufügen oder die Umstellung auf Klammern, können Sie weglassen ref (Tp), um das Problem zu vermeiden:

template <typename Tp, typename... Us> 
Tp bar(Us&&... us) { 
    return {std::forward<Us>(us)...}; 
} 
+0

Weglassen 'Tp' in return-Anweisung verwirft' Tp's mit 'explicit' Bauer ... generische Funktion ist nicht mehr so ​​generisch –

+0

@KarolWozniak Das ist ein guter Punkt. Auf der anderen Seite benötigt das, was Sie bereits haben (alle drei Versionen), einen barrierefreien Copy- oder Move-Konstruktor, also ist es nicht so generisch, wie es möglicherweise sein könnte. Ich habe keine gute Alternative, die alle möglichen Typen umfasst, sorry. – hvd