name - using c++ 11
¿Cuál es el beneficio de std:: literals::.. ser espacios de nombres en línea? (1)
El literal s
definido por el usuario no "choca" entre los seconds
y la string
, incluso si ambos están dentro del alcance, porque se sobrecargan como cualquier otro par de funciones, en sus diferentes listas de argumentos:
string operator "" s(const char* str, size_t len);
seconds operator "" s(unsigned long long sec);
Esto se evidencia ejecutando esta prueba:
void test1()
{
using namespace std;
auto str = "text"s;
auto sec = 1s;
}
Con el using namespace std
, ambos sufijos están dentro del alcance y, sin embargo, no entran en conflicto entre sí.
Entonces, ¿por qué el inline namespace
baila?
El fundamento es permitir que el programador exponga la cantidad de nombres definidos estándar que se desee. En la prueba anterior, he "importado" toda la biblioteca estándar en la test
, o al menos tanto como se ha incluido #.
test1()
no habría funcionado si los namespace literals
no estuvieran en inline
.
Aquí hay una forma más restringida de usar los literales, sin importar el estándar completo:
void test2()
{
using namespace std::literals;
auto str = "text"s;
auto sec = 1s;
string str2; // error, string not declared.
}
Esto incluye todos los literales definidos estándar, pero no (por ejemplo) std::string
.
test2()
no funcionaría si el namespace string_literals
no estuviera en inline
y el namespace chrono_literals
no estuviera en inline
.
También puede optar por exponer los literales de cadena, y no los literales de crono:
void test3()
{
using namespace std::string_literals;
auto str = "text"s;
auto sec = 1s; // error
}
O simplemente los literales crono y no los literales de cuerda:
void test4()
{
using namespace std::chrono_literals;
auto str = "text"s; // error
auto sec = 1s;
}
Finalmente, hay una manera de exponer todos los nombres de crono y los chrono_literals:
void test5()
{
using namespace std::chrono;
auto str = "text"s; // error
auto sec = 1s;
}
test5()
requiere este pedacito de magia:
namespace chrono { // hoist the literals into namespace std::chrono
using namespace literals::chrono_literals;
}
En resumen, los inline namespace
son una herramienta para que todas estas opciones estén disponibles para el desarrollador.
Actualizar
El OP hace algunas buenas preguntas de seguimiento a continuación. Son (con suerte) abordados en esta actualización.
¿No es una buena idea
using namespace std
?
Depende. El using namespace
nunca es una buena idea en el ámbito global en un encabezado que debe formar parte de una biblioteca de propósito general. No desea forzar un montón de identificadores en el espacio de nombres global de su usuario. Ese espacio de nombres pertenece a su usuario.
Un ámbito global que using namespace
puede estar bien en un encabezado si el encabezado solo existe para la aplicación que está escribiendo, y si está de acuerdo con usted, tiene todos esos identificadores disponibles para todo lo que incluye ese encabezado. Pero cuantos más identificadores descargues en tu ámbito global, más probable es que entren en conflicto con algo. using namespace std;
trae un montón de identificadores, y traerá aún más con cada nueva versión del estándar. Así que no recomiendo using namespace std;
a nivel global en un encabezado incluso para su propia aplicación.
Sin embargo, pude ver el using namespace std::literals
o el using namespace std::chrono_literals
en el ámbito global en un encabezado, pero solo para un encabezado de aplicación, no para un encabezado de biblioteca.
Me gusta usar las directivas en el alcance de la función, ya que la importación de identificadores se limita al alcance de la función. Con tal límite, si surge un conflicto, es mucho más fácil de solucionar. Y es menos probable que suceda en primer lugar.
Los literales definidos estándar probablemente nunca entren en conflicto entre sí (no lo hacen hoy). Pero nunca se sabe...
Los literales definidos estándar nunca entrarán en conflicto con los literales definidos por el usuario porque los literales definidos estándar nunca comenzarán con _
, y los literales definidos por el usuario tienen que comenzar con _
.
Además, para los desarrolladores de bibliotecas, ¿es necesario (o una buena práctica) no tener sobrecargas conflictivas dentro de varios espacios de nombres en línea de una biblioteca grande?
Esta es una muy buena pregunta, y creo que el jurado aún está deliberando sobre esto. Sin embargo, resulta que estoy desarrollando una biblioteca que a propósito tiene literales en conflicto definidos por el usuario en diferentes espacios de nombres en línea.
https://github.com/HowardHinnant/date
#include "date.h"
#include "julian.h"
#include <iostream>
int
main()
{
using namespace date::literals;
using namespace julian::literals;
auto ymd = 2017_y/jan/10;
auto jymd = julian::year_month_day{ymd};
std::cout << ymd << ''/n'';
std::cout << jymd << ''/n'';
}
El código anterior no se compila con este mensaje de error:
test.cpp:10:20: error: call to ''operator""_y'' is ambiguous
auto ymd = 2017_y/jan/10;
^
../date/date.h:1637:1: note: candidate function
operator "" _y(unsigned long long y) NOEXCEPT
^
../date/julian.h:1344:1: note: candidate function
operator "" _y(unsigned long long y) NOEXCEPT
^
El _y
literal se usa para crear el year
en esta biblioteca. Y esta biblioteca tiene un calendario gregoriano (en "date.h") y un calendario juliano (en "julian.h"). Cada uno de estos calendarios tiene una clase year
: ( date::year
y julian::year
). Son tipos diferentes porque el año gregoriano no es lo mismo que un año juliano. Pero todavía es conveniente nombrarlos el year
y darles a ambos un literal.
Si quito el using namespace julian::literals;
a partir del código anterior se compila y genera:
2017-01-10
2016-12-28
que es una demostración de que 2016-12-28 Julian es el mismo día que 2017-01-10 Gregorian. Y esto también es una demostración gráfica de que el mismo día puede tener diferentes años en diferentes calendarios.
Solo el tiempo dirá si mi uso de _y
s conflictivo será problemático. Hasta la fecha no ha sido. Sin embargo, no muchas personas han usado esta biblioteca con calendarios no gregorianos.
En el C ++ - Estándar (por ejemplo, N4594) hay dos definiciones para el operator""s
:
namespace std {
...
inline namespace literals {
inline namespace chrono_literals {
// 20.15.5.8, suffixes for duration literals
constexpr chrono::seconds operator"" (unsiged long long);
y string
por supuesto:
namespace std {
....
inline namespace literals {
inline namespace string_literals {
// 21.3.5, suffix for basic_string literals:
string operator "" s(const char* str, size_t len);
Me pregunto qué se gana con esos espacios de nombres (y todos los demás espacios de nombres dentro de std::literals
), si están en inline
.
Pensé que estaban dentro de espacios de nombres separados para que no entren en conflicto entre sí. Pero cuando están en inline
, esta motivación se deshace, ¿verdad? Edición: porque Bjarne explica que la motivación principal es la "versión de la biblioteca", pero esto no encaja aquí.
Puedo ver que las sobrecargas de "Segundos" y "Cadena" son distintas y por lo tanto no entran en conflicto. Pero, ¿entrarían en conflicto si las sobrecargas fueran las mismas? ¿O tomar el namespace
(en inline
?) Impide que de alguna manera?
Por lo tanto, ¿qué se gana al estar en un inline namespace
en inline namespace
? ¿Cómo, como apunta @Columbo a continuación, se resuelve la sobrecarga en los espacios de nombres en línea, y se chocan?