用户定义的字面量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 定义名称。 在上面的测试中,我已经将整个 std 库“导入”到了 test
中,或者至少已经包含了尽可能多的内容。
如果没有将 namespace literals
设为 inline
,那么 test1()
将无法工作。
以下是使用字面量的更受限制的方法,而无需导入整个 std:
void test2()
{
using namespace std::literals;
auto str = "text"s;
auto sec = 1s;
string str2;
}
这将引入所有的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;
}
或者只使用chrono字面值而不是字符串字面值:
void test4()
{
using namespace std::chrono_literals;
auto str = "text"s;
auto sec = 1s;
}
最终有一种方法可以公开所有chrono名称和chrono字面值:
void test5()
{
using namespace std::chrono;
auto str = "text"s;
auto sec = 1s;
}
test5()
需要这个神奇的小操作:
namespace chrono { // hoist the literals into namespace std::chrono
using namespace literals::chrono_literals;
}
总之,
内联命名空间
是为开发人员提供所有这些选项的工具。
更新
OP在下面提出了一些很好的后续问题。这些问题(希望如此)在此更新中得到解答。
using namespace std
是否不是一个好主意?
这取决于情况。在一个用作通用目的库的头文件的全局作用域中使用
using namespace
绝不是一个好主意。您不希望将一堆标识符强制放入用户的全局命名空间中。那个命名空间属于用户。
如果头文件仅适用于您正在编写的应用程序,并且您允许针对包括该头文件的所有内容都可用的所有标识符,则可以在头文件中使用全局范围的
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';
}
以上代码编译失败并显示以下错误信息:
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”使用是否会有问题。到目前为止还没有出现过问题。但是很少有人使用这个库与非公历日历一起使用。
unsiged long long
,也许这是unsigned ...
的打字错误。 - agc