Search code examples
c++constexprone-definition-ruleinline-functionsfunction-definition

Why is constexpr solving duplicated definition?


I have a header file where string are defined as static global.

namespace space {
#define NAME(P) static std::string const s_##P = #P
        NAME(foo); NAME(bar); //... other values
#undef  NAME
}

In another header, an enum is defined and a template specialization provides the mapping between the enum and a string in space.

enum class letter { alpha, beta };

template<letter> std::string const & mapping();
#define MAPPING(P1,P2) template<> std::string const & mapping<letter::P1>() { return space::s_##P2; }
        MAPPING(alpha,foo)
        MAPPING(beta,bar)
#undef  MAPPING

The above code doesn't link when the header is included in more than one translation unit because the specializations definitions do not match - due to global redefinition per translation unit (I guess).

Wrapping the mapping functions in anonymous namespace or adding static keyword solves the linking issue but then the compiler complains that the functions are defined but not used [-Wunused-function].

template<letter> static std::string const & mapping();

But, defining the specializations as constexpr, there is no longer any link or warning issue.

template<letter> std::string const & mapping();
#define MAPPING(P1,P2) template<> constexpr std::string const & mapping<letter::P1>() { return space::s_##P2; }

I understand why the non-static version fails at link time and why the static version works and triggers warnings. But I don't understand why the constexpr specifier solves both issues.

Can you please give an explanation and even better, a rational in the standard ?


Solution

  • Function template specializations are functions, and are therefore subject to the one-definition rule in the same manner as functions that are not template specializations.

    The linker errors you saw when the functions were declared neither static nor constexpr were due to multiple definitions of the same function template specializations which each had external linkage.

    When you added static, you made the linkage internal. This made it safe for each translation unit to contain its own copy of the definitions. However, in any TU in which those functions were not called, the compiler knew that (due to internal linkage) they could not be called from any other TU either, making them unused.

    With constexpr, the functions become inline implicitly according to the standard, but their linkage is not affected. Since they are inline, you can have multiple definitions, but since they have external linkage, the compiler does not complain when one TU does not use them.