Los literales definidos por el usuario deben comenzar con un guión bajo.
Esta es una regla más o menos universalmente conocida que se puede encontrar en todos los sitios redactados hablando de literales de usuario. También es una regla que yo (¿y posiblemente otros?) He ignorado descaradamente desde entonces sobre una base de "qué mierda". Ahora, por supuesto, eso no es correcto. En el sentido más estricto, esto utiliza un identificador reservado y, por lo tanto, invoca Comportamiento indefinido (aunque prácticamente no obtiene un encogimiento de hombros del compilador).
Entonces, reflexionando si debería continuar ignorando deliberadamente esa parte (en mi opinión inútil) de la norma o no, decidí mirar lo que realmente está escrito. Porque, ya sabes, qué importa lo que todos sepan . Lo que importa es lo que está escrito en el estándar.
[over.literal]
establece que "algunos" identificadores de sufijo literales están reservados, vinculando a [usrlit.suffix]
. El último establece que todos están reservados, excepto aquellos que comienzan con un guión bajo. OK, eso es exactamente lo que ya sabíamos, escrito explícitamente (o más bien, escrito al revés).
Además, [over.literal]
contiene una Nota que sugiere algo obvio pero preocupante:
a excepción de las restricciones descritas anteriormente, son funciones comunes de espacio de nombres y plantillas de funciones
Bueno, claro que lo son. En ninguna parte dice que no lo son, entonces, ¿qué más esperarías que sean?
Pero espera un momento. [lex.name]
establece explícitamente que cada identificador que comienza con un guión bajo en el espacio de nombres global está reservado.
Ahora, un operador literal generalmente, a menos que lo coloque explícitamente en un espacio de nombres (¡lo cual, creo que nadie hace !?), está muy en el espacio de nombres global. Entonces, el nombre, que debe comenzar con un guión bajo, está reservado. No se menciona una excepción especial. Entonces, cada nombre (con guión bajo o sin él) es un nombre reservado.
¿Se espera que ponga literales definidos por el usuario en un espacio de nombres porque el uso "normal" (subrayado o no) está usando un nombre reservado?
fuente
_km
(por kilómetros) en el espacio de nombresudl
. ¿Entonces un literal para 5 km parece ...5udl::_km
?using
están las declaraciones. En el ámbito donde necesita usar el literal, tenga una instrucción de uso que lo importe.Respuestas:
Sí: la combinación de prohibir el uso
_
como el inicio de un identificador global junto con la exigencia de UDL no estándar para comenzar_
significa que no puede colocarlos en el espacio de nombres global. Pero no debe ensuciar el espacio de nombres global con cosas, especialmente UDL, por lo que no debería ser un gran problema.El idioma tradicional, como lo usa el estándar, es colocar UDL en un
literals
espacio de nombres (y si tiene diferentes conjuntos de UDL, entonces los coloca en diferentesinline namespaces
debajo de ese espacio de nombres). Eseliterals
espacio de nombres suele estar debajo del principal. Cuando desee utilizar un conjunto particular de UDL, invoqueusing namespace my_namespace::literals
o el subespacio de nombres que contenga su conjunto literal de elección.Esto es importante porque las UDL tienden a estar muy abreviadas. El estándar, por ejemplo, utiliza
s
parastd::string
, pero también parastd::chrono::duration
de segundos. Si bien se aplican a diferentes tipos de literales (s
aplicado a una cadena es una cadena, mientras ques
aplicado a un número es una duración), a veces puede ser confuso leer código que usa literales abreviados. Por lo tanto, no debe lanzar literales a todos los usuarios de su biblioteca; deberían optar por usarlos.Mediante el uso de diferentes espacios de nombres para estos (
std::literals::string_literals
ystd::literals::chrono_literals
), el usuario puede ser franco sobre qué conjuntos de literales desean en qué partes del código.fuente
_Foo
sufijos, que se omitieron de la pregunta pero son bastante problemáticos.Esta es una buena pregunta, y no estoy seguro acerca de la respuesta, pero creo que la respuesta es "no, no es UB" basado en una lectura particular del estándar.
[lex.name] /3.2 lee:
Ahora, claramente, la restricción "como un nombre en el espacio de nombres global" debe leerse como que se aplica a toda la regla, no solo a cómo la implementación puede usar el nombre. Es decir, su significado no es
"cada identificador que comienza con un guión bajo está reservado para la implementación, Y la implementación puede usar identificadores como nombres en el espacio de nombres global"
sino más bien
"el uso de cualquier identificador que comience con un guión bajo como nombre en el espacio de nombres global está reservado para la implementación".
(Si creyéramos la primera interpretación, significaría que nadie podría declarar una función llamada
my_namespace::_foo
, por ejemplo).Según la segunda interpretación, algo así como una declaración global de
operator""_foo
(en el ámbito global) es legal, porque dicha declaración no se usa_foo
como un nombre. Más bien, el identificador es solo una parte del nombre real, que esoperator""_foo
(que no comienza con un guión bajo).fuente
void operator+(foo, bar)
, donde claramente el nombre de la función no es un identificador, sino un nombre. Lo mismo vale paraoperator "" _foo
ser el nombre en nuestro caso.Claramente no.
El siguiente es el uso idiomático (y por lo tanto definitivamente "normal") de UDL, y está bien definido de acuerdo con la regla que acaba de enumerar:
Ha enumerado casos problemáticos y estoy de acuerdo con su evaluación sobre su validez, pero se pueden evitar fácilmente en un código idiomático de C ++, por lo que no veo completamente el problema con la redacción actual, incluso si fue potencialmente accidental.
Según el ejemplo en [over.literal] / 8, incluso podemos usar letras mayúsculas después del guión bajo:
Por lo tanto, lo único problemático parece ser el hecho de que el estándar hace que el espacio en blanco entre
""
el nombre UDL sea significativo.fuente
operator""_Bq
está bien (lo que significa queoperator""_K
también está bien). El truco es omitir el espacio entre""
y el sufijo. Ver C ++ 17 [over.literal] / 8.Sí, definir su propio literal definido por el usuario en el espacio de nombres global da como resultado un programa mal formado.
No me he encontrado con esto yo mismo, porque trato de seguir la regla:
No coloque nada (además de
main
espacios de nombres yextern "C"
cosas para la estabilidad ABI) en el espacio de nombres global.Esto también significa que no puede usarlo
_CAPS
como su nombre literal, incluso en un espacio de nombres.Espacios de nombres en línea llamados
literals
son una excelente manera de empaquetar los operadores literales definidos por el usuario. Se pueden importar donde desee usarlo sin tener que nombrar exactamente qué literales desea, o si importa todo el espacio de nombres también obtendrá los literales.Esto sigue cómo la
std
biblioteca maneja los literales también, por lo que debería ser familiar para los usuarios de su código.fuente
Dado el literal con sufijo
_X
, la gramática llama a_X
un "identificador" .Entonces, sí: el estándar, presumiblemente sin darse cuenta, ha hecho imposible crear un UDT de alcance global, o UDT que comiencen con una letra mayúscula, en un programa bien definido. (Tenga en cuenta que lo primero no es algo que generalmente quiera hacer de todos modos)
Esto no se puede resolver editorialmente: los nombres de los literales definidos por el usuario tendrían que tener su propio "espacio de nombres" léxico que evite conflictos con (por ejemplo) nombres de funciones proporcionadas por la implementación. En mi opinión, sin embargo, hubiera sido bueno que hubiera una nota no normativa en alguna parte, señalando las consecuencias de estas reglas y señalando que son deliberadas.
fuente
int _x(int);
(en global) no causará un error de compilación?_x
deberían estar exentos[lex.name]
. Si no he entendido bien, lo que sigue es basura . Si la implementación ya había declarado una funciónint _x(int);
en el ámbito global (nombre reservado así que bien), entonces un usuario declaradolong double operator "" _x(long double);
(por ejemplo) obtendría un error de compilación.