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 namespace
s are a tool to make all of these options available to the developer.
Update
The OP asks some good followup questions below. They are (hopefully) addressed in this update.
Is using namespace std
not a good idea?
It depends. A using namespace
is never a good idea at global scope in a header that is meant to be part of a general purpose library. You don't want to force a bunch of identifiers into your user's global namespace. That namespace belongs to your user.
A global scope using namespace
can be ok in a header if the header only exists for the application you are writing, and if it is ok with you that you have all of those identifiers available for everything that includes that header. But the more identifiers you dump into your global scope, the more likely it is that they will conflict with something. using namespace std;
brings in a bunch of identifiers, and will bring in even more with each new release of the standard. So I don't recommend using namespace std;
at global scope in a header even for your own application.
However I could see using namespace std::literals
or using namespace std::chrono_literals
at global scope in a header, but only for an application header, not a library header.
I like to use using
directives at function scope as then the import of identifiers is limited to the scope of the function. With such a limit, if a conflict does arise, it is much easier to fix. And it is less likely to happen in the first place.
std-defined literals will probably never conflict with one another (they do not today). But you never know...
std-defined literals will never conflict with user-defined literals because std-defined literals will never start with _
, and user-defined literals have to start with _
.
Also, for library developers, is it necessary (or good practice) to have no conflicting overloads inside several inline namespaces of a large library?
This is a really good question, and I posit that the jury is still out on this one. However I just happen to be developing a library that purposefully has conflicting user-defined literals in different inline namespaces!
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';
}
The above code fails to compile with this error message:
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
^
The _y
literal is used to create year
in this library. And this library has both a Gregorian calendar (in "date.h") and a Julian calendar (in "julian.h"). Each of these calendars has a year
class: (date::year
and julian::year
). They are different types because the Gregorian year is not the same thing as a Julian year. But it is still convenient to name them both year
and to give them both a _y
literal.
If I remove the using namespace julian::literals;
from the code above then it compiles and outputs:
2017-01-10
2016-12-28
which is a demonstration that 2016-12-28 Julian is the same day as 2017-01-10 Gregorian. And this is also a graphic demonstration that the same day can have different years in different calendars.
Only time will tell if my use of conflicting _y
s will be problematic. To date it hasn't been. However not many people have used this library with non-Gregorian calendars.
unsiged long long
, perhaps that's a typo forunsigned ...
. – Simonton