view raw
towi towi - 9 months ago 58
C++ Question

What is the benefit of std::literals::.. being inline namespaces?

In the C++-Standard (eg. N4594) there are two definitions for


namespace std {
inline namespace literals {
inline namespace chrono_literals {
//, suffixes for duration literals
constexpr chrono::seconds operator"" (unsiged long long);

of course:

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);

I wonder what is gained from those namespaces (and all the other namespaces inside
), if they are

I thought they were inside separate namespaces so they do not conflict with each other. But when they are
, this motivation is undone, right? Edit: Because Bjarne explains the main motivation is "library versioning", but this does not fit here.

I can see that the overloads for "Seconds" and "String" are distinct and therefor do not conflict. But would they conflict if the overloads were the same? Or does take the (
prevents that somehow?

Therefore, what is gained from them being in an
inline namespace
at all?
How, as @Columbo points out below, are overloading across inline namespaces resolved, and do they clash?


The user-defined literal s does not "clash" between seconds and string, even if they are both in scope, because they overload like any other pair of functions, on their different argument lists:

string  operator "" s(const char* str, size_t len);
seconds operator "" s(unsigned long long sec);

This is evidenced by running this test:

void test1()
    using namespace std;
    auto str = "text"s;
    auto sec = 1s;

With using namespace std, both suffixes are in scope, and yet do not conflict with each other.

So why the inline namespace dance?

The rationale is to allow the programmer to expose as few std-defined names as desired. In the test above, I've "imported" the entire std library into test, or at least as much as has been #included.

test1() would not have worked had namespace literals not been inline.

Here is a more restricted way to use the literals, without importing the entire std:

void test2()
    using namespace std::literals;
    auto str = "text"s;
    auto sec = 1s;
    string str2;  // error, string not declared.

This brings in all std-defined literals, but not (for example) std::string.

test2() would not work if namespace string_literals was not inline and namespace chrono_literals was not inline.

You can also choose to just expose the string literals, and not the chrono literals:

void test3()
    using namespace std::string_literals;
    auto str = "text"s;
    auto sec = 1s;   // error

Or just the chrono literals and not the string literals:

void test4()
    using namespace std::chrono_literals;
    auto str = "text"s;   // error
    auto sec = 1s;

Finally there is a way to expose all of the chrono names and the chrono_literals:

void test5()
    using namespace std::chrono;
    auto str = "text"s;   // error
    auto sec = 1s;

test5() requires this bit of magic:

namespace chrono { // hoist the literals into namespace std::chrono
    using namespace literals::chrono_literals;

In summary, the inline namespaces are a tool to make all of these options available to the developer.