incorrectly - multi character constant c++
std:: movimiento de cadena literal-¿qué compilador es el correcto? (2)
Comencemos dividiendo esto en dos partes, para que podamos analizar cada una por separado:
#include <string>
void foo()
{
auto x = std::move("");
std::string s(x);
}
La segunda parte, que inicializa la cadena de x
(cualquiera que sea el tipo que pueda ser) no es realmente el problema o pregunta que tenemos aquí. La pregunta en cuestión (al menos como me parece a mí) es la primera línea, donde tratamos de vincular una referencia rvalue a un literal de cadena.
La parte relevante del estándar para eso sería [dcl.init.ref]/5
(§8.5.3 / 5, al menos en la mayoría de las versiones del estándar de C ++ que he visto).
Esto comienza con:
Una referencia al tipo '''' cv1 T1 '''' se inicializa mediante una expresión del tipo '''' cv2 T2 '''' de la siguiente manera.
Eso es seguido por una lista de viñetas. El primer elemento solo cubre referencias de valor l, por lo que lo ignoraremos. El segundo elemento dice:
si la expresión del inicializador
- es un prvalue de clase xvalue (pero no un campo de bits), prvalue de matriz o función lvalue [...]
- tiene tipo de clase (es decir,T2
es un tipo de clase) [...]
- De lo contrario - siT1
oT2
es un tipo de clase y T1 no es no es referencia relacionada con T2 [...]
Claramente, ninguno de esos aplica. Un literal de cadena no es un valor x, valor de clase pr, valor de matriz ni valor de función, ni tiene tipo de clase.
Eso solo sale:
Si
T1
está relacionado con la referencia aT2
:
- cv1 será la misma calificación cv, o mayor calificación cv que, cv2 , y
- si la referencia es una referencia rvalue, la expresión del inicializador no será un valor l.
Dado que, en este caso, el tipo de resultado de la conversión está siendo deducido por el compilador, estará relacionado con la referencia al tipo de inicializador. En este caso, como dice la parte que he enfatizado, la expresión del inicializador no puede ser un valor l.
Eso solo deja la pregunta de si un literal de cadena es un valor l. Al menos de forma directa, no puedo encontrar de inmediato la sección del estándar de C ++ que dice que son (no hay mención de ello en la sección de literales de cadenas). Si está ausente, el siguiente paso sería mirar el documento base (el estándar C) que establece claramente que los literales de cadena son valores l (N1570, §6.5.1 / 4)
Un literal de cadena es una expresión primaria. Es un lvalue con tipo como se detalla en 6.4.5.
Desearía poder encontrar una declaración directa al respecto en el estándar de C ++ (estoy bastante seguro de que debería existir), pero por alguna razón no lo encuentro en este momento.
Dado el siguiente código:
#include <string>
void foo()
{
std::string s(std::move(""));
}
Esto se compila con apple clang (xcode 7) y no con Visual Studio 2015, que genera el siguiente error:
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 por el momento que el movimiento es redundante, ¿qué implementación estándar de la biblioteca es la más correcta en este caso?
Mi impresión es que el tipo de ""
es const char[1]
por lo que std::move
debería devolver std::remove_reference<const char[1]&>::type&&
que sería const char[1]&&
.
Me parece que esto debería decaer para const char*
.
¿O no entiendo las reglas?
Esto parece un error de Visual Studio. Esto se reduce a std::move y si miramos la página cppreference tiene la siguiente firma:
template< class T >
typename std::remove_reference<T>::type&& move( T&& t );
y regresa:
static_cast<typename std::remove_reference<T>::type&&>(t)
que coincide con el borrador de la sección estándar de C ++ 20.2.4
adelante / mover helpers [forward].
Utilizando el código que obtuve aquí , podemos ver el siguiente ejemplo:
#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 siguiente respuesta de gcc y clang usando Wandbox :
xvalue
y esta respuesta de Visual Studio usando webcompiler :
lvalue
de ahí el error de Visual Studio para el código original:
No puede vincular un lvalue a una referencia rvalue
cuando intenta vincular el resultado de static_cast<typename std::remove_reference<T>::type&&>(t)
a std::remove_reference<T>::type&&
que es el valor de retorno de std::move
.
No veo ningún motivo por el cual static_cast
deba generar un lvalue como lo hace en el caso de Visual Studio.