Consider the following code:
const int a = 0;
const std::string b = "hi";
inline void f_a1()
{
std::cout << a;
}
inline void f_b1()
{
std::cout << b;
}
inline void f_a2()
{
std::cout << &a;
}
inline void f_b2()
{
std::cout << &b;
}
Assume this code exists in a header file that will be included in multiple translation units.
My understanding of inline functions is that they must be exactly the same in every translation unit.
My understanding of constants as used above, is that they are implictly static
ie internal linkage. These means each translation unit gets its own copy.
As the inline functions above rely on these constants, which of these functions, if any, are correct?
If included into multiple translation units, the only function that is valid is f_a1
.
The relevant clause is [basic.def.odr]/6, which states that an inline
function can appear in multiple translation units, but only given that:
[...] a name can refer to a non-volatile
const
object with internal or no linkage if the object has the same literal type in all definitions of D,
and the object is initialized with a constant expression (5.19), and the object is not odr-used, and the
object has the same value in all definitions of D;
As the objects are const
, they have internal linkage per [basic.link]/3:
A name having namespace scope (3.3.6) has internal linkage if it is the name of [...]
— a non-volatile variable that is explicitly declared const or constexpr and neither explicitly declared
extern nor previously declared to have external linkage [...]
However, taking the address of or forming a reference to a variable (e.g. for argument passing) is odr-use, so f_a2
and f_b2
are invalid. f_b1
is also invalid, as the ostream
output operator for std::string
takes its argument by reference; and even if it took its argument by value the implicitly called copy constructor would take its argument by reference. f_a1
is OK because the int
stream-out operator takes its argument by value, and copying the value of an int const
is not odr-use.