2015-12-08 8 views
25

Dato il seguente codice:std :: move of string literal - quale compilatore è corretto?

#include <string> 
void foo() 
{ 
    std::string s(std::move("")); 
} 

Questo compila con clangore mela (Xcode 7) e non con Visual Studio 2015 che genera il seguente errore:

error C2440: 'return': cannot convert from 'const char [1]' to 'const char (&&)[1]' 
note: You cannot bind an lvalue to an rvalue reference 
main.cpp(4): note: see reference to function template instantiation 'const char (&&std::move<const char(&)[1]>(_Ty) noexcept)[1]' being compiled 
    with 
    [ 
     _Ty=const char (&)[1] 
    ] 

Ignorando per il momento che la mossa è ridondante, quale implementazione della libreria standard è la più corretta in questo caso?

La mia sensazione è che il tipo di "" è const char[1] così std::move dovrebbe restituire std::remove_reference<const char[1]&>::type&& che sarebbe const char[1]&&.

Mi sembra che questo decadrebbe a const char*.

Oppure ho frainteso le regole?

+11

Compila anche su [g ++ 5.2.0] (http://coliru.stacked-crooked.com/a/5547385a86e74d19). La mia scommessa è che Visual Studio è sbagliato, dato che di solito è ... – Claudiu

+4

Penso che tu abbia riassunto tutto bene nella tua domanda. MSVC è sbagliato, non è la prima volta. – SergeyA

+3

Che errore produce MSVC, in particolare? – edmz

risposta

10

Questo sembra un bug di Visual Studio. Questo si riduce al std::move e se guardiamo la pagina cppreference ha la seguente firma:

template< class T > 
typename std::remove_reference<T>::type&& move(T&& t); 

e restituisce:

static_cast<typename std::remove_reference<T>::type&&>(t) 

che corrisponde al progetto C++ sezione standard 20.2.4 avanti aiutanti/move [inoltrare].

Utilizzando la code I grabbed from here possiamo vedere il seguente esempio:

#include <iostream> 

template<typename T> 
struct value_category { 
    // Or can be an integral or enum value 
    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"; 
}; 

// Double parens for ensuring we inspect an expression, 
// not an entity 
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value 


int main() 
{ 
    std::cout << VALUE_CATEGORY(static_cast<std::remove_reference<const char[1]>::type&&>("")) << std::endl ; 

} 

genera la seguente risposta da gcc e clang utilizzando Wandbox:

xvalue 

e questa risposta da Visual Studio utilizzando webcompiler:

lvalue 

henc una e l'errore da Visual Studio per il codice originale:

You cannot bind an lvalue to an rvalue reference

quando si tenta di legare il risultato di static_cast<typename std::remove_reference<T>::type&&>(t)-std::remove_reference<T>::type&& che è il valore di ritorno di std::move.

Non vedo alcun motivo per cui lo static_cast dovrebbe generare un lvalue come nel caso di Visual Studio.

+0

Incredibile! –

+0

Con un margine ristretto ho accettato la risposta di Shafik poiché indica la pistola fumante in modo inequivocabile al colpevole. –

+0

@EugeneZavidovsky Mi sento ancora come se mi mancasse qualcosa, questa sembra una differenza così fondamentale. Speravo di trovare un rapporto sui difetti ma finora non ho avuto fortuna. –

4

Cominciamo rompendo questo in due pezzi, in modo che possiamo analizzare ogni parte:

#include <string> 
void foo() 
{ 
    auto x = std::move(""); 
    std::string s(x); 
} 

La seconda parte, che inizializza la stringa da x (qualunque sia il tipo che potrebbe capitare di essere) non è davvero il problema o la domanda a portata di mano qui. La domanda in questione (almeno come mi sembra) è la prima riga, in cui cerchiamo di associare un riferimento di rvalue a una stringa letterale.

La parte rilevante dello standard per questo sarebbe [dcl.init.ref]/5 (§8.5.3/5, almeno nella maggior parte delle versioni dello standard C++ che ho visto).

Questo inizia con:

A reference to type ''cv1 T1'' is initialized by an expression of type ''cv2 T2'' as follows.

che è seguita da un elenco puntato. Il primo elemento copre solo i riferimenti lvalue, quindi lo ignoreremo. Il secondo elemento dice:

if the initializer expression
- is an xvalue (but not a bit-field) class prvalue, array prvalue or function lvalue[...]
- has class type (i.e., T2 is a class type)[...]
- Otherwise - if T1 or T2 is a class type and T1 is not is not reference related to T2 [...]

Chiaramente nessuno di questi si applica. Una stringa letterale non è un valore x, un valore di classe, un valore di matrice o un valore di funzione, né ha un tipo di classe.

Ciò lascia solo:

If T1 is reference-related to T2 :
- cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2, and
- if the reference is an rvalue reference, the initializer expression shall not be an lvalue.

Poiché, in questo caso, il tipo del risultato della conversione viene dedotto dal compilatore, esso sarà essere riferimento relativi alla tipologia del inizializzatore. In questo caso, come dice la parte che ho enfatizzato, l'espressione di inizializzazione non può essere un lvalue.

Ciò lascia solo la questione se una stringa letterale è un lvalue. Almeno alla fine, non riesco a trovare immediatamente la sezione dello standard C++ che dice di esserlo (non ne parliamo nella sezione sui letterali stringa). Se è assente, il passo successivo sarebbe quello di guardare il documento di base (lo standard C) in cui si afferma chiaramente che le stringhe letterali sono lvalue (N1570, §6.5.1/4)

A string literal is a primary expression. It is an lvalue with type as detailed in 6.4.5.

Io vorrei poter trova una dichiarazione diretta in tal senso nello standard C++ (sono abbastanza sicuro che dovrebbe esistere), ma per qualche ragione non lo sto trovando proprio ora.

+4

La divisione non è valida, poiché la detrazione automatica non mantiene il tipo esatto. –

+0

Ma aspettate, l'argomento per std :: move è un riferimento universale poiché è valutato nel contesto dedotto. Ciò rende legale il valore letter string letterale, giusto? –

+2

Trovato quel riferimento C++ per te: 5.1.1/1: "Un letterale è un'espressione primaria, il suo tipo dipende dalla sua forma (2.14). Un letterale stringa è un valore lovalue, tutti gli altri valori letterali sono valori preregolati." –

Problemi correlati