std :: literals :: ..作为内联命名空间有什么好处?

tow*_*owi 21 c++ namespaces literals inline-namespaces c++14

在C++ - Standard(例如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);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

我想知道从这些命名空间(以及其他所有命名空间std::literals)中获得了什么,如果它们是inline.

我认为它们位于不同的命名空间内,因此它们不会相互冲突.但是当它们存在时inline,这种动机就会消失,对吧?编辑:因为Bjarne解释主要动机是"库版本化",但这不适合这里.

我可以看到"Seconds"和"String"的重载是不同的,因此不会发生冲突.但如果重载相同,它们会发生冲突吗?或者采取(inline?)namespace以某种方式阻止?

因此,他们从中获得了什么inline namespace呢?正如@Columbo在下面指出的那样,如何解决内联命名空间的重载问题,它们是否会发生冲突?

How*_*ant 36

用户定义的文字s不会在seconds和之间"冲突" string,即使它们都在范围内,因为它们在其不同的参数列表上像任何其他函数对一样重载:

string  operator "" s(const char* str, size_t len);
seconds operator "" s(unsigned long long sec);
Run Code Online (Sandbox Code Playgroud)

这可以通过运行此测试来证明:

void test1()
{
    using namespace std;
    auto str = "text"s;
    auto sec = 1s;
}
Run Code Online (Sandbox Code Playgroud)

使用时using namespace std,两个后缀都在范围内,但不会相互冲突.

那么为什么inline namespace跳舞呢?

理由是允许程序员根据需要公开少量std定义的名称.在上面的测试中,我将整个std库"导入"了test,或者至少与#included一样多.

test1()就不用工作了namespace literals没有inline.

这是一种使用文字的更受限制的方式,而不导入整个std:

void test2()
{
    using namespace std::literals;
    auto str = "text"s;
    auto sec = 1s;
    string str2;  // error, string not declared.
}
Run Code Online (Sandbox Code Playgroud)

这会引入所有std定义的文字,但不会(例如)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
}
Run Code Online (Sandbox Code Playgroud)

或者只是时间文字,而不是字符串文字:

void test4()
{
    using namespace std::chrono_literals;
    auto str = "text"s;   // error
    auto sec = 1s;
}
Run Code Online (Sandbox Code Playgroud)

最后有一种方法可以公开所有的计时名称 chrono_literals:

void test5()
{
    using namespace std::chrono;
    auto str = "text"s;   // error
    auto sec = 1s;
}
Run Code Online (Sandbox Code Playgroud)

test5() 需要这一点魔力:

namespace chrono { // hoist the literals into namespace std::chrono
    using namespace literals::chrono_literals;
}
Run Code Online (Sandbox Code Playgroud)

总之,inline namespaces是一个工具,可以让开发人员使用所有这些选项.

更新

OP在下面提出了一些很好的后续问题.它们(希望)在本次更新中得到解决.

using namespace std不是一个好主意?

这取决于.对于using namespace通用库的一部分,A 在全局范围内绝不是一个好主意.您不希望将大量标识符强制插入用户的全局命名空间.该命名空间属于您的用户.

using namespace如果标题仅存在于您正在编写的应用程序中,则可以在标题中使用全局范围,如果您可以使用所有这些标识符可用于包含该标题的所有内容.但是,您转储到全局范围的标识符越多,它们与某些内容冲突的可能性就越大. using namespace std;带来了一堆标识符,并且每个新版本的标准版本都会带来更多标识符.因此using namespace std;,即使对于您自己的应用程序,我也不建议在标题的全局范围内.

但是我可以在标题中看到using namespace std::literals或者using namespace std::chrono_literals在全局范围内,但仅适用于应用程序标头,而不是库标头.

我喜欢using在函数范围使用指令,因为标识符的导入仅限于函数的范围.有了这样的限制,如果确实发生冲突,则更容易修复.并且它不太可能首先发生.

std定义的文字可能永远不会相互冲突(它们今天不会).但你永远不知道......

std定义的文字永远不会与用户定义的文字冲突,因为std定义的文字永远不会开始_,用户定义的文字必须从头开始_.

此外,对于库开发人员,是否有必要(或良好实践)在大型库的多个内联命名空间内没有冲突的重载?

这是一个非常好的问题,我认为陪审团仍然在这个问题上.然而,我恰好正在开发一个有意在不同的内联命名空间中具有冲突的用户定义文字的库!

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';
}
Run Code Online (Sandbox Code Playgroud)

上面的代码无法使用此错误消息进行编译:

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
^
Run Code Online (Sandbox Code Playgroud)

_y文字是用来创建year这个库.这个图书馆既有格里高利历("date.h"),也有朱利安历("julian.h").这些日历中的每一个都有一个year类:( date::yearjulian::year).它们是不同的类型,因为格里高利年与朱利安年不同.但是将它们命名为两者year并给它们两个_y文字仍然很方便.

如果我using namespace julian::literals;从上面的代码中删除它然后编译并输出:

2017-01-10
2016-12-28
Run Code Online (Sandbox Code Playgroud)

这是2016-12-28 Julian与2017-01-10 Gregorian同日的示范.这也是一个图形演示,同一天可以在不同的日历中有不同的年份.

只有时间才能证明我对冲突_y的使用是否有问题.到目前为止还没有.然而,没有多少人使用这个库与非格里高利历.

  • 我读完了前几段,就像"这可能是霍华德":) (4认同)