在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);
和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的主要动机是"库版本控制",但这不适合这里。
我可以看到" Seconds"和"字符串"是不同的,因此不冲突。但如果重载相同,它们会发生冲突吗?或者采取(inline
?)namespace
以某种方式阻止?
因此,他们从inline namespace
获得的是什么?
正如@Columbo在下面指出的那样,如何解决内联命名空间的重载问题,它们会发生冲突吗?
答案 0 :(得分:35)
用户定义的文字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
,或至少与#include一样多。
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定义的文字,但不会(例如)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
是一个让开发人员可以使用所有这些选项的工具。
<强>更新强>
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
。这个图书馆既有公历又有#34; date.h&#34;和Julian日历(&#34; julian.h&#34;)。这些日历中的每一个都有一个year
类:{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
是否有问题。到目前为止还没有。然而,没有多少人将这个图书馆与非公历日历一起使用。