2015-12-27 18 views
14

ich eine C++ Klasse, die die folgende Schnittstelle:Sollte ein C++ temporäres konstant sein?

class F { 
public: 
    F(int n, int d); 
    // no other constructors/assignment constructors defined 
    F& operator *= (const F&); 
    F& operator *= (int); 
    int n() const; 
    int d() const; 
}; 

Und ich habe den folgenden Code:

const F a{3, 7}; 
const F b{5, 10}; 
auto result = F{a} *= b; // How does this compile? 

unter Visual Studio (VS) 2013 erstellt die Kommentarzeile ohne Fehler. Unter VS2015 wird Fehler C2678 hergestellt:

error C2678: binary '*=': no operator found 
    which takes a left-hand operand of type 'const F' 
    (or there is no acceptable conversion) 
note: could be 'F &F::operator *=(const F &)' 
note: or  'F &F::operator *=(int)' 
note: while trying to match the argument list '(const F, const F)' 

Meine Erwartung war, dass F{a} eine nicht konstante temporäre Kopie von aoperator *= (b) angewandt werden würde, zu dem schaffen würde, wonach das temporäre Objekt würde result zugeordnet werden. Ich habe nicht erwartet, dass das Temporäre eine Konstante ist. Interessanterweise: auto result = F(a) *= b; kompiliert ohne Fehler in VS2015, die ich dachte, sollte semantisch gleich sein.

Meine Frage ist: Welches Verhalten ist richtig VS2015 oder VS2013 & warum?

Vielen Dank

+1

Sieht aus wie ein Fehler auf den ersten Blick, 'F {a}' sollte nicht-const sein –

+0

Kannst du einen MCVE posten, nur um sicherzustellen, dass es keine anderen Faktoren gibt? –

+2

Offensichtlich verlor es seine Murmeln, wenn es versuchte, auf eine (const F, const F) Initialisierungsliste zu schließen. Verwenden Sie stattdessen 'F (a)'. Verwenden Sie connect.microsoft.com, um Fehler zu melden. –

Antwort

0

Meine Gedanken, es ist ein Fehler in VS2015, denn wenn Sie benutzerdefinierten Copykonstruktor angeben:

F(const F&); 

oder machen Variable a nicht konstanter Code erfolgreich kompiliert werden.

Sieht aus, als ob die Objektkonstanz von a in das neu erstellte Objekt übertragen wird.

+0

Was ist, wenn Sie die Kopie ctor explizit machen? Es scheint mir, es benutzt es als Besetzung, weil es keine Notwendigkeit für eine echte Kopie gibt. –

8

Visual Studio 2015 produziert nicht das richtige Ergebnis für:

F{a} 

Das Ergebnis sollte eine prvalue (gcc und klirren beide haben dieses Ergebnis) sein, aber es ist ein L-Wert zu erzeugen. Ich verwende die folgende modifizierte Version des Codes des OP dieses Ergebnis zu liefern:

#include <iostream> 

class F { 
public: 
    F(int n, int d) :n_(n), d_(d) {}; 
    F(const F&) = default ; 
    F& operator *= (const F&){return *this; } 
    F& operator *= (int) { return *this; } 
    int n() const { return n_ ; } 
    int d() const { return d_ ; } 
    int n_, d_ ; 
}; 

template<typename T> 
struct value_category { 
    static constexpr auto value = "prvalue"; 
}; 

template<typename T> 
struct value_category<T&> { 
    static constexpr auto value = "lvalue"; 
}; 

template<typename T> 
struct value_category<T&&> { 
    static constexpr auto value = "xvalue"; 
}; 

#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value 

int main() 
{ 
    const F a{3, 7}; 
    const F b{5, 10}; 
    std::cout << "\n" << VALUE_CATEGORY(F{a}) << "\n"; 
} 

Hutspitze zu Luc Danton for the VALUE_CATEGORY() code.

Visual Studio webcompiler verwendet, die eine relativ neue Version hat produziert:

lvalue 

die const in diesem Fall müssen die Fehler sehen wir zu produzieren. Während beide gcc und Klirren (see it live) produzieren:

prvalue 

Dies kann std::move of string literal - which compiler is correct? gleichermaßen rätselhaft Visual Studio Fehler in Beziehung gesetzt werden.

Hinweis können wir das gleiche Problem bekommen mit gcc und Klappern mit einem const F:

using cF = const F ; 
auto result = cF{a} *= b; 

so nicht nur Visual Studio wird uns die falsche Wert Kategorie geben, aber es willkürlich auch einen cv-Qualifier Hinzufügen .

Wie Hans in seinen Kommentaren zu Ihrer Frage unter Verwendung von F(a) notiert hat, werden die erwarteten Ergebnisse erzeugt, da ein Prvalue korrekt erzeugt wird.

Der entsprechende Abschnitt des Entwurfs C++ Standard Abschnitt 5.2.3[expr.type.conv] die sagt:

In ähnlicher Weise ein einfacher-Typ-Spezifizierer oder Typname-Angabe durch eine braced- gefolgt init-list erstellt ein temporäres Objekt des angegebenen Typs direct-list-initialized (8.5.4) mit der angegebenen brained-init-Liste und sein Wert ist das temporäre Objekt als prvalue.

Hinweis, soweit ich das sagen kann, ist nicht die "old MSVC lvalue cast bug". Die Lösung für dieses Problem besteht darin, /Zc:rvalueCast zu verwenden, das dieses Problem nicht behebt. Dieses Problem unterscheidet sich auch in der inkorrekten Hinzufügung eines cv-Qualifikators, der, soweit ich weiß, nicht mit der vorherigen Ausgabe geschieht.

+0

oh, es ist der alte MSVC lvalue Cast Bug. Dargestellt, dass sie das nicht verewigt haben, um die Initialisierung aufzulisten ... –

+0

@ M.M wie weißt du? Soweit ich die Lösung [wie hier erwähnt] (http://stackoverflow.com/a/26508755/1708801) sagen kann, ist '/ Zc: rvalueCast', die dieses Problem nicht beheben. –

+0

Ich weiß aus dem Lesen Ihrer Antwort (?) –

0

Visual C++ hat seit einiger Zeit einen Fehler, bei dem ein Identity Cast keine temporäre generiert, sondern auf die ursprüngliche Variable verweist.

Bugreport hier: identity cast to non-reference type violates standard

+0

Ich glaube nicht, dass es der gleiche Fehler ist, soweit ich das verstehe, ist das das gleiche Problem wie [diese Frage] (http://stackoverflow.com/a/26508755/1708801) und die Lösung ist zu verwenden '/ Zc: rvalueCast', die das Problem hier nicht behebt. –

0

Von http://en.cppreference.com/w/cpp/language/copy_elision:

Unter den folgenden Umständen werden die Compiler erlaubt die Urheber wegzulassen und bewegen-Konstrukteuren von Klassenobjekten auch wenn Kopieren/Verschieben Konstruktor und der Destruktor haben beobachtbare Nebenwirkungen.

.......

Wenn ein nameless temporäres, nicht auf irgendwelche Referenzen gebunden wäre bewegt oder in ein Objekt des gleichen Typs kopiert (oberste Ebene ignoriert CV- Qualifikation), wird die Kopie/Verschiebung weggelassen. Wenn das temporäre konstruiert ist, wird es direkt in dem Speicher erstellt, in dem andernfalls verschoben oder kopiert werden würde. Wenn das namenlose Temporary das Argument einer return-Anweisung ist, wird diese Variante der Kopie elision als RVO, "Rückgabewert-Optimierung" bekannt.

So der Compiler hat die Option, die Kopie zu ignorieren (die in diesem Fall als eine implizite Umwandlung in nicht-const-Typ fungieren würde).

Verwandte Themen