В стандарте C ++ (например, N4594) есть два определения 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);
а также string
конечно:
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);
Интересно, что получается из этих пространств имен (и всех других пространств имен внутри std::literals
), если они inline
,
Я думал, что они были в отдельных пространствах имен, поэтому они не конфликтуют друг с другом. Но когда они inline
эта мотивация отменена, верно? Редактировать: Так как Бьярне объясняет основной мотивацией является «управление версиями библиотеки», но здесь это не подходит.
Я вижу, что перегрузки для «Seconds» и «String» различны и поэтому не конфликтуют. Но будут ли они конфликтовать, если перегрузки были одинаковыми? Или взять (inline
?) namespace
что то мешает?
Следовательно, что получается от них в inline namespace
совсем?
Как, как указывает @Columbo ниже, разрешена перегрузка между встроенными пространствами имен и конфликтуют ли они?
Пользовательский литерал s
не «конфликтует» между seconds
а также string
, даже если они оба находятся в области видимости, потому что они перегружаются, как и любая другая пара функций, в своих разных списках аргументов:
string operator "" s(const char* str, size_t len);
seconds operator "" s(unsigned long long sec);
Это подтверждается запуском этого теста:
void test1()
{
using namespace std;
auto str = "text"s;
auto sec = 1s;
}
С using namespace std
оба суффикса находятся в области видимости, но не конфликтуют друг с другом.
Так почему inline namespace
танцевать?
Логическое обоснование состоит в том, чтобы позволить программисту выставлять как можно меньше стандартных имен. В тесте выше я «импортировал» всю библиотеку std в test
или хотя бы столько, сколько было включено.
test1()
не работал бы имел namespace literals
не был inline
,
Вот более ограниченный способ использования литералов, без импорта всего стандартного std:
void test2()
{
using namespace std::literals;
auto str = "text"s;
auto sec = 1s;
string str2; // error, string not declared.
}
Это приносит все стандартные литералы, но не (например) std::string
,
test2()
не будет работать, если namespace string_literals
не было inline
а также namespace chrono_literals
не было inline
,
Вы также можете выбрать просто выставить строковые литералы, а не хронологические литералы:
void test3()
{
using namespace std::string_literals;
auto str = "text"s;
auto sec = 1s; // error
}
Или просто хронологические литералы, а не строковые литералы:
void test4()
{
using namespace std::chrono_literals;
auto str = "text"s; // error
auto sec = 1s;
}
Наконец, есть способ выставить все имена хроно а также chrono_literals:
void test5()
{
using namespace std::chrono;
auto str = "text"s; // error
auto sec = 1s;
}
test5()
требует немного магии:
namespace chrono { // hoist the literals into namespace std::chrono
using namespace literals::chrono_literals;
}
Таким образом, inline namespace
Это инструмент, который делает все эти опции доступными для разработчика.
Обновить
ОП задает несколько хороших последующих вопросов ниже. Они (надеюсь) рассматриваются в этом обновлении.
Является
using namespace std
не хорошая идея?
Это зависит. using namespace
никогда не является хорошей идеей для глобальной области видимости в заголовке, который должен быть частью библиотеки общего назначения. Вы не хотите вводить несколько идентификаторов в глобальное пространство имен вашего пользователя. Это пространство имен принадлежит вашему пользователю.
Глобальная сфера using namespace
может быть хорошо в заголовке, если заголовок существует только для приложения, которое вы пишете, и если это нормально для вас, что у вас есть все эти идентификаторы, доступные для всего, что включает этот заголовок. Но чем больше идентификаторов вы добавите в свою глобальную область, тем больше вероятность того, что они будут конфликтовать с чем-то. using namespace std;
приносит в гроздь идентификаторов, и будет вводить еще больше с каждым новым выпуском стандарта. Поэтому я не рекомендую using namespace std;
в глобальном масштабе в заголовке даже для вашего собственного приложения.
Однако я мог видеть using namespace std::literals
или же using namespace std::chrono_literals
в глобальной области видимости в заголовке, но только для заголовка приложения, а не заголовка библиотеки.
Я люблю использовать using
директивы в области действия функции, так как импорт идентификаторов ограничен областью действия функции. С таким ограничением, если конфликт действительно возникает, его гораздо легче исправить. И это менее вероятно произойдет в первую очередь.
Стандартные литералы будут наверное никогда не конфликтуйте друг с другом (они не сегодня). Но ты никогда не знаешь …
Стандартные литералы будут никогда конфликт с пользовательскими литералами, потому что стандартные литералы никогда не начнутся с _
и пользовательские литералы иметь начать с _
,
Кроме того, для разработчиков библиотек необходимо (или хорошая практика) не иметь конфликтующих перегрузок внутри нескольких встроенных пространств имен большой библиотеки?
Это действительно хороший вопрос, и я полагаю, что жюри еще не решено. Однако я просто разрабатываю библиотеку, которая целенаправленно имеет конфликтующие пользовательские литералы в разных встроенных пространствах имен!
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';
}
Приведенный выше код не может быть скомпилирован с этим сообщением об ошибке:
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
^
_y
литерал используется для создания year
в этой библиотеке. И у этой библиотеки есть и григорианский календарь (в «date.h») и юлианский календарь (в «julian.h»). Каждый из этих календарей имеет year
учебный класс: (date::year
а также julian::year
). Это разные типы, потому что григорианский год — это не то же самое, что юлианский год. Но все же удобно назвать их обоих year
и дать им обоим _y
буквальный.
Если я удалю using namespace julian::literals;
из кода выше, то он компилирует и выводит:
2017-01-10
2016-12-28
что является демонстрацией того, что 2016-12-28 юлианский день совпадает с григорианским 2017-01-10. И это также наглядная демонстрация того, что один и тот же день может иметь разные годы в разных календарях.
Только время покажет, если мое использование противоречит _y
с будет проблематично. На сегодняшний день этого не было. Однако не многие люди использовали эту библиотеку с негригорианскими календарями.
Других решений пока нет …