Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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

One for std::chrono::seconds :

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

and one for 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); 

I wonder what is gained from those namespaces (and all the other namespaces inside std::literals), if they are inline.

I thought they were inside separate namespaces so they do not conflict with each other. But when they are inline, 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 (inline?) namespace 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?

like image 805
towi Avatar asked Aug 21 '16 14:08

towi


People also ask

What are inline namespaces used for?

Inline namespaces (C++ 11) You can use inline namespaces as a versioning mechanism to manage changes to the public interface of a library. For example, you can create a single parent namespace, and encapsulate each version of the interface in its own namespace nested inside the parent.

Is using namespace std a good idea?

The statement using namespace std is generally considered bad practice. The alternative to this statement is to specify the namespace to which the identifier belongs using the scope operator(::) each time we declare a type.

Why we should not use using namespace std in C++?

While this practice is okay for example code, pulling in the entire std namespace into the global namespace is not good as it defeats the purpose of namespaces and can lead to name collisions. This situation is called namespace pollution.

What is the use of namespace in C++?

Namespace in C++ is the declarative part where the scope of identifiers like functions, the name of types, classes, variables, etc., are declared. The code generally has multiple libraries, and the namespace helps in avoiding the ambiguity that may occur when two identifiers have the same name.

Why it is important to write “using namespace std” in C++?

Why it is important to write “using namespace std” in C++ program? In this article, we will discuss the use of “using namespace std” in the C++ program. As the same name can’t be given to multiple variables, functions, classes, etc. in the same scope.

What are the advantages of using inline namespace?

But by using inline with namespaces there is no need to type the entire namespace as given above or use the “using” directive. Support of Library : The inline namespace mechanism is intended to support library evolution by providing a mechanism that supports a form of versioning. Refer this for details.

What does the inline specifier do in C++?

The inline specifier makes the declarations from the nested namespace appear exactly as if they had been declared in the enclosing namespace. This means it drags out the declaration (“var” in the above example) from a nested namespace to the containing namespace.

Can constexpr member functions have a literal type?

Only literal types may be used as parameters to or returned from constexpr functions. Only literal classes may have constexpr member functions. ↑ Alisdair Meredith. "Deprecate the is_literal Trait". Deprecating Vestigial Library Parts in C++17.


1 Answers

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.

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 _ys will be problematic. To date it hasn't been. However not many people have used this library with non-Gregorian calendars.

like image 79
Howard Hinnant Avatar answered Sep 29 '22 15:09

Howard Hinnant