std::literals::.. 成为内联命名空间有什么好处?
What is the benefit of std::literals::.. being inline namespaces?
在 C++ 标准(例如 N4594)中,operator""s
有两个定义:
std::chrono::seconds
一个:
namespace std {
...
inline namespace literals {
inline namespace chrono_literals {
// 20.15.5.8, suffixes for duration literals
constexpr chrono::seconds operator "" s(unsigned long long);
还有一个 std::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
时,这种动机就消失了,对吧? 编辑: 因为 Bjarne explains 主要动机是 "library versioning",但这不适合这里。
我可以看到 "Seconds" 和 "String" 的重载是不同的,因此不会冲突。但如果重载相同,它们会发生冲突吗?或者采取 (inline
?) namespace
以某种方式阻止这种情况?
因此,他们在 inline namespace
中到底得到了什么?
正如@Columbo 在下面指出的那样,跨内联命名空间的重载是如何解决的,它们是否会发生冲突?
user-defined 文字 s
不会 "clash" 在 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-defined 名称。在上面的测试中,我将整个 std 库 "imported" 放入 test
,或者至少与 #included 一样多。
如果 namespace literals
不是 inline
,test1()
就不会工作。
这是一种使用文字的更受限制的方式,无需导入整个 std:
void test2()
{
using namespace std::literals;
auto str = "text"s;
auto sec = 1s;
string str2; // error, string not declared.
}
这会引入所有 std-defined 文字,但不会引入(例如)std::string
。
如果 namespace string_literals
不是 inline
并且 namespace chrono_literals
不是 inline
.,test2()
将不起作用
您还可以选择只公开字符串文字,而不是计时文字:
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
是一种工具,可让开发人员使用所有这些选项。
更新
OP 在下面提出了一些很好的后续问题。他们(希望)在这次更新中得到解决。
Is using namespace std
not a good idea?
视情况而定。 using namespace
在 header 的全局范围内从来都不是一个好主意,它意味着成为通用库的一部分。您不想将一堆标识符强加到用户的全局名称空间中。该命名空间属于您的用户。
如果 header 只存在于您正在编写的应用程序中,并且如果您认为您拥有所有这些标识符可用于包含 header 的所有内容。但是,您转储到全局范围内的标识符越多,它们与某些东西发生冲突的可能性就越大。 using namespace std;
引入了 bunch 标识符,并且会随着标准的每个新版本引入更多。所以我不建议 using namespace std;
在 header 的全局范围内,即使是你自己的应用程序也是如此。
但是我可以在 header 的全局范围内看到 using namespace std::literals
或 using namespace std::chrono_literals
,但仅针对应用程序 header,而不是库 header .
我喜欢在函数范围内使用 using
指令,因为标识符的导入仅限于函数范围。有了这样的限制,如果确实发生了冲突,修复起来就容易多了。而且一开始就不太可能发生。
std-defined 字面值 可能 永远不会相互冲突(它们今天不会)。但你永远不知道...
std-defined 文字 永远不会 与 user-defined 文字冲突,因为 std-defined 文字永远不会以 _
开头,并且 user-defined 文字 有 以 _
.
开头
Also, for library developers, is it necessary (or good practice) to have no conflicting overloads inside several inline namespaces of a large library?
这是一个非常好的问题,我认为这个问题还没有定论。然而,我恰好正在开发一个库,该库 有目的地 在不同的内联命名空间中具有冲突的 user-defined 文字!
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
class:(date::year
和 julian::year
)。它们是不同的类型,因为公历年与儒略年不同。但是将它们都命名为 year
并给它们一个 _y
文字仍然很方便。
如果我从上面的代码中删除 using namespace julian::literals;
然后它编译并输出:
2017-01-10
2016-12-28
证明 2016-12-28 Julian 与 2017-01-10 Gregorian 是同一天。而且这也是一个图解展示,同一天在不同的历法中可以有不同的年份。
只有时间才能证明我对冲突 _y
的使用是否会出现问题。迄今为止还没有。然而,没有多少人将这个库与 non-Gregorian 日历一起使用。
在 C++ 标准(例如 N4594)中,operator""s
有两个定义:
std::chrono::seconds
一个:
namespace std {
...
inline namespace literals {
inline namespace chrono_literals {
// 20.15.5.8, suffixes for duration literals
constexpr chrono::seconds operator "" s(unsigned long long);
还有一个 std::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
时,这种动机就消失了,对吧? 编辑: 因为 Bjarne explains 主要动机是 "library versioning",但这不适合这里。
我可以看到 "Seconds" 和 "String" 的重载是不同的,因此不会冲突。但如果重载相同,它们会发生冲突吗?或者采取 (inline
?) namespace
以某种方式阻止这种情况?
因此,他们在 inline namespace
中到底得到了什么?
正如@Columbo 在下面指出的那样,跨内联命名空间的重载是如何解决的,它们是否会发生冲突?
user-defined 文字 s
不会 "clash" 在 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-defined 名称。在上面的测试中,我将整个 std 库 "imported" 放入 test
,或者至少与 #included 一样多。
namespace literals
不是 inline
,test1()
就不会工作。
这是一种使用文字的更受限制的方式,无需导入整个 std:
void test2()
{
using namespace std::literals;
auto str = "text"s;
auto sec = 1s;
string str2; // error, string not declared.
}
这会引入所有 std-defined 文字,但不会引入(例如)std::string
。
namespace string_literals
不是 inline
并且 namespace chrono_literals
不是 inline
.,test2()
将不起作用
您还可以选择只公开字符串文字,而不是计时文字:
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
是一种工具,可让开发人员使用所有这些选项。
更新
OP 在下面提出了一些很好的后续问题。他们(希望)在这次更新中得到解决。
Is
using namespace std
not a good idea?
视情况而定。 using namespace
在 header 的全局范围内从来都不是一个好主意,它意味着成为通用库的一部分。您不想将一堆标识符强加到用户的全局名称空间中。该命名空间属于您的用户。
如果 header 只存在于您正在编写的应用程序中,并且如果您认为您拥有所有这些标识符可用于包含 header 的所有内容。但是,您转储到全局范围内的标识符越多,它们与某些东西发生冲突的可能性就越大。 using namespace std;
引入了 bunch 标识符,并且会随着标准的每个新版本引入更多。所以我不建议 using namespace std;
在 header 的全局范围内,即使是你自己的应用程序也是如此。
但是我可以在 header 的全局范围内看到 using namespace std::literals
或 using namespace std::chrono_literals
,但仅针对应用程序 header,而不是库 header .
我喜欢在函数范围内使用 using
指令,因为标识符的导入仅限于函数范围。有了这样的限制,如果确实发生了冲突,修复起来就容易多了。而且一开始就不太可能发生。
std-defined 字面值 可能 永远不会相互冲突(它们今天不会)。但你永远不知道...
std-defined 文字 永远不会 与 user-defined 文字冲突,因为 std-defined 文字永远不会以 _
开头,并且 user-defined 文字 有 以 _
.
Also, for library developers, is it necessary (or good practice) to have no conflicting overloads inside several inline namespaces of a large library?
这是一个非常好的问题,我认为这个问题还没有定论。然而,我恰好正在开发一个库,该库 有目的地 在不同的内联命名空间中具有冲突的 user-defined 文字!
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
class:(date::year
和 julian::year
)。它们是不同的类型,因为公历年与儒略年不同。但是将它们都命名为 year
并给它们一个 _y
文字仍然很方便。
如果我从上面的代码中删除 using namespace julian::literals;
然后它编译并输出:
2017-01-10
2016-12-28
证明 2016-12-28 Julian 与 2017-01-10 Gregorian 是同一天。而且这也是一个图解展示,同一天在不同的历法中可以有不同的年份。
只有时间才能证明我对冲突 _y
的使用是否会出现问题。迄今为止还没有。然而,没有多少人将这个库与 non-Gregorian 日历一起使用。