C++ std::literals的好处是什么:。。是内联名称空间吗?
在C++标准(如N4594)中,C++ std::literals的好处是什么:。。是内联名称空间吗?,c++,namespaces,c++14,literals,inline-namespaces,C++,Namespaces,C++14,Literals,Inline Namespaces,在C++标准(如N4594)中,运算符有两种定义: 一个用于标准:计时:秒 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: names
运算符有两种定义:
一个用于标准:计时:秒
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
?)名称空间
是否以某种方式防止了这种情况
因此,在内联名称空间中使用它们有什么好处?
正如@Columbo在下面指出的,跨内联名称空间的重载是如何解决的,它们是否冲突?用户定义的文本s
在秒和字符串之间不会“冲突”,即使它们都在作用域内,因为它们在不同的参数列表上像任何其他函数对一样重载:
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;
}
当使用namespace std
时,两个后缀都在范围内,但不会相互冲突
那么为什么内联名称空间会跳舞呢
其基本原理是允许程序员根据需要公开尽可能少的std定义的名称。在上面的测试中,我已经将整个std库“导入”到test
,或者至少导入了与包含的一样多的内容
test1()
如果没有内联名称空间文本
就不会工作
以下是一种更严格的使用文本的方法,无需导入整个std:
void test2()
{
using namespace std::literals;
auto str = "text"s;
auto sec = 1s;
string str2; // error, string not declared.
}
这会引入所有std定义的文本,但不会(例如)std::string
如果名称空间字符串(文字)
不是内联的
并且名称空间时间(文字)
不是内联的
,则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;
}
或者只是计时文字,而不是字符串文字:
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名称和chrono_文字:
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;
}
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;
}
总之,inlinenamespace
s是开发人员可以使用的工具
更新
OP在下面提出了一些很好的后续问题。他们(希望)在本更新中得到解决
使用命名空间std
不是一个好主意吗
视情况而定。在作为通用库一部分的标头中,在全局范围内使用命名空间从来都不是一个好主意。您不希望将一堆标识符强制放入用户的全局命名空间中。该命名空间属于您的用户
如果报头只存在于您正在编写的应用程序中,并且您认为包含该报头的所有标识符都可用,那么在报头中使用名称空间的全局作用域就可以了。但是,您在全局范围中转储的标识符越多,它们与某些内容冲突的可能性就越大<代码>使用名称空间std
引入了一系列标识符,并且随着标准的每一个新版本,将引入更多的标识符。所以我不建议使用名称空间std代码>在标题中的全局范围内,即使对于您自己的应用程序也是如此
但是,我可以看到使用名称空间std::literals
或使用名称空间std::chrono_literals
在报头的全局范围内,但仅适用于应用程序报头,而不是库报头
我喜欢在函数范围内使用指令,因为标识符的导入仅限于函数范围。有了这样的限制,如果确实发生冲突,修复起来就容易多了。而且从一开始就不太可能发生
std定义的文字可能永远不会相互冲突(现在不会)。但你永远不知道
std定义的文字永远不会与用户定义的文字冲突,因为std定义的文字永远不会以\uu
开头,而用户定义的文字必须以\u
开头
此外,对于库开发人员来说,在大型库的几个内联名称空间中是否有必要(或良好实践)没有冲突的重载
这是一个非常好的问题,我认为这个问题还没有定论。然而,我恰好正在开发一个库,该库故意在不同的内联名称空间中具有冲突的用户定义文本
\u y
文字用于在此库中创建年份
。这个图书馆有公历(date.h)和儒略历(Julian.h)。这些日历中的每一个都有一个year
类:(date::year
和julian::year
)。它们是不同的类型,因为公历年和儒略年不是一回事。但是,将它们都命名为year
还是很方便的,并且给它们都加上\u y
文字
如果我使用名称空间julian::literals删除代码>从上面的代码中编译并输出:
2017-01-10
2016-12-28
这证明2016-12-28朱利安与2017-01-10格里高利是同一天。这也是一个图形演示,同一天在不同的日历中可以有不同的年份
只有时间才能证明我使用冲突的\y
s是否有问题。到目前为止还没有。然而,很少有人将该库与非Gre一起使用