简体   繁体   中英

C++ Template specialization for constant variables

I am currently trying to have a compile-time constant variable that is template specialized for several types. Currently, I am using constant expressions such as the following generalized example:

template<typename T>
constexpr T GENERAL_CONSTANT = T(0.01);

template<> constexpr float GENERAL_CONSTANT<float> = float(0.02);

template<> constexpr double GENERAL_CONSTANT<double> = double(0.03);

This code however only seems to work on some compilers/linkers. It will compile and work correctly for Clang 9.0.0 for Windows 10, Clang 6.0.0 for Windows 10, Clang 6.0.0 for Ubuntu 18.04, and GCC for Ubuntu 18.04. But has given the similar multiple redefinition errors in several other configurations such as Clang 10.0.0 on Windows 10 or Clang 10.0.0 on Unix, as well as a few others. The errors will often look similar to this:

/usr/bin/ld: <some path to a.cpp> multiple definition of `GENERAL_CONSTANT<double>'; <some path to a.cpp>: first defined here
/usr/bin/ld: <some path to a.cpp> multiple definition of `GENERAL_CONSTANT<float>'; <some path to a.cpp>: first defined here

Where 'a.cpp' is the file that uses the constant, but does not define them. So given that this error is happening inconsistently depending on compiler and machine, I was curious if this is a non-standard approach to this problem, and if that is true, what approach should I take instead?

const qualification of a variable template (and constexpr does makes an object const ) does not imply internal linkage , [basic.link]/p3 :

A name having namespace scope has internal linkage if it is the name of

  • a variable, variable template , function, or function template that is explicitly declared static ; or

  • a non-template variable of non-volatile const-qualified type , unless [...]

It seems to be a recent change ( CWG 2387 ):

Notes from the December, 2018 teleconference:

CWG felt that a const type should not affect the linkage of a variable template or its instances.

and that explains the difference that you observed in Clang-10.

As a solution, mark the variable template and its specializations either static or inline . The former forces internal linkage, while the latter excludes the variable template instances from the one definition rule allowing for multiple definitions provided they are in separate translation units.

This code does compile as is. See This Demo . Specialized template variables will still compile into the binary, even when marked constexpr . Since you are using the header and thus these variables in multiple translation units, you are getting a linker error. You can tell this by the fact that ld is giving you the error.

Marking the specializations inline will prevent this from happening and fix the issue:

template<typename T>
constexpr T GENERAL_CONSTANT = T(0.01);

template<> inline constexpr float GENERAL_CONSTANT<float> = float(0.02);

template<> inline constexpr double GENERAL_CONSTANT<double> = double(0.03);

This is a compiler bug, because constexpr implies inline for variables and functions but apparently, certain compiler versions mess this up for template specializatons.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM