I noticed a strange behavior when I was working with a constexpr function. I reduced the code to a simplified example. Two functions are called from two different translation units (module A and B).
#include <iostream>
int mod_a();
int mod_b();
int main()
{
std::cout << "mod_a(): " << mod_a() << "\n";
std::cout << "mod_b(): " << mod_b() << "\n";
std::cout << std::endl;
return 0;
}
The modules look similar. This is mod_a.cpp:
constexpr int X = 3;
constexpr int Y = 4;
#include "common.h"
int mod_a()
{
return get_product();
}
Only some internal constants differ. This is mod_b.cpp:
constexpr int X = 6;
constexpr int Y = 7;
#include "common.h"
int mod_b()
{
return get_product();
}
Both modules use a common constexpr function which is defined in "common.h":
/* static */ constexpr int get_product()
{
return X * Y;
}
I was very astonished that both functions return 12. Due to the #include directive (which should only be some source code inclusion), I supposed that there is no interaction between both modules.
When I defined get_product also to be static, the behavior was as expected:
mod_a() returned 12,
mod_b() returned 42.
I also looked Jason Turner's episode 312 of C++ Weekly: Stop Using 'constexpr' (And Use This Instead!) at https://www.youtube.com/watch?v=4pKtPWcl1Go.
The advice to use generally static constexpr is a good hint.
But I still wonder if the behavior which I noticed without the static keyword is well defined. Or is it UB? Or is it a compiler bug?
Instead of the constexpr function I also tried a C-style macro #define get_product() (X*Y) which showed me also the expected results (12 and 42).
Take care
michaeL
This program ill-formed:
XandYhave internal linkage since they areconstvariables at namespace scope. This means that both definitions ofconstexpr int get_product()(which is implicitlyinline) violate the one definition rule:And obviously these constants have different values.
What's happening is both
mod_aandmod_bare callingget_productat runtime.get_productis implicitly inline, so one of the definitions is chosen and the other is discarded. What gcc seems to do is take the first definition found:It's as if
get_productisn'tconstexpr, since it is getting called at runtime.But if you were to enable optimisations (or force
get_product()to be called at compile time, like withconstexpr int result = get_product(); return result;), the results are as you would "expect":(Though this is still UB, and the correct fix is to make the functions
static)