C++14 added variable templates, which define groups of related variables. In the standard library variable templates are used for accessing each type trait's value member:
template<class T>
inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
C++17 added inline variables to better support header-only libraries, which can be included in multiple source files within the same application (identical inline variable definitions are allowed in separate translation units). But as far as variable templates are concerned, they are allowed to have more than one definition in a program anyway. So is there any reason to declare variable templates inline if they are already exempt from ODR?
As long as many people have focused on constexpr vs inline constexpr difference, which is another interesting question, I'd like to disregard constexpr for the purpose of this discussion.
template <typename T>
bool myVar = sizeof(T) > 1;
How does it differ from the following:
template <typename T>
inline bool myVar = sizeof(T) > 1;
Since link can rotten, copping content of https://quuxplusone.github.io/blog/2022/07/08/inline-constexpr here as an answer. I'm not an author. Browser plugin "Copy selection as markdown" did nice job.
An example where
inline constexprmakes a differenceThe C++11 and C++14 standard libraries defined many constexpr global variables like this:
In C++17, all of these constexpr variables were respecified as
inline constexprvariables. Theinlinekeyword here means the same thing as it does on aninlinefunction: “This entity might be defined in multiple TUs; all those definitions are identical; merge them into one definition at link time.” If you look at the generated code for one of these variables in C++14, you’ll see something like this (Godbolt):Whereas in C++17, you’ll see this instead:
The critical word in the latter snippet is
comdat; it means “Hey linker! Instead of concatenating the text of all.rodata._ZSt8in_placesections together, you should deduplicate them, so that only one such section is included in the final executable!” There’s another minor difference in the name-mangling ofstd::in_placeitself: as aninline constexprvariable it gets mangled into_ZSt8in_place, but as a non-inline(and thereforestatic) variable it gets mangled into_ZStL8in_placewith anL. Clang’s name-mangling code has this to say about theL:On the C++ Slack, Ed Catmur showed an example of how this difference can be observed. This is a contrived example, for sure, but it does concretely demonstrate the difference between mere
constexpr(internal linkage, one entity per TU) andinline constexpr(external linkage, one entity for the entire program).The difference between the last two command lines is whether the linker sees
alpha.oormain.ofirst, and therefore whether it chooses to keep the definition ofinline const void *f()fromalpha.cppormain.cpp. If it keeps the one fromalpha.cpp, then the result of the expressionalpha()()will be the address ofx-from-alpha.cpp. But, thanks to the inliner, the assertion inmainwill be comparing that address against the address ofx-from-main.cpp. Whenxis markedinline, there’s only one entityxin the whole program, so the twoxs are the same and the assertion succeeds. But whenxis a plain oldconstexprvariable, there are two different (internal-linkage)xs with two different addresses, and so the assertion fails.You can reproduce this behavior with libstdc++, by swapping the variable
xfor a C++14 standard library variable likestd::piecewise_construct. The assertion inmainwill pass when compiled with-std=c++17and fail when compiled with-std=c++14. This is because libstdc++ makes these variables conditionallyinlinedepending on the language mode (source):LLVM/Clang’s libc++, on the other hand, doesn’t conditionalize their code based on the language mode (source):
I speculate that this was done in order to reduce the confusion that could result if a program was compiled partly as C++14 and partly as C++17. It’s bad enough that a program’s behavior can change (in this contrived scenario) depending on whether it’s compiled as C++14 or C++17; imagine the additional confusion if some parts of the program believed there was only one
std::piecewise_constructand other parts believed there were several.libc++ aggressively drops support for compilers older than about two years. I expect that at some point all supported compilers will permit
inline constexpras an extension even in C++11 mode, and then libc++ will be free to addinlineto all its global variables in one fell swoop.