问题
If we have a header file widget.hpp
with the contents below:
constexpr int foo = 10;
struct widget
{
int bars[foo];
};
...and we have two translation units generated from two source files which both only include widget.hpp
, does this violate the one definition rule (more specifically, does the use of foo
violate the one definition rule)?
foo
has internal linkage but it is also a constant expression. From my reading of 3.2.6 in the C++11 standard, which I will quote below, this is well-formed if requirement #2 is not referring solely to static data members.
3.2.6 requirement #2:
in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after matching of partial template specialization (14.8.3), except 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
回答1:
The only place I can see any room for question about your case would be whether or not your use of foo
qualifies as odr-used
or not. Perhaps the easiest way to clarify at least the intent would be to quote the corresponding section of n1337 (immediately followed the official standard, mostly cleaning up some phrasing, such as in this case):
[...] a name can refer to a 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 value (but not the address) of the object is used, and the object has the same value in all definitions of D;
Your use clearly meets all these requirements.
foo
has typeint
in every case.foo
is initialized with a constant expression.- you use only the value, not the address, of
foo
. foo
has the same value in all defintions ofwidget
.
That said, you'd probably be better off changing foos
to a std::vector
instead:
struct widget {
std::vector<int> bars;
widget : bars(foo) {}
};
回答2:
As for the multiple definitions of foo
, I don't think foo
is odr-used because it satisfies the requirements for appearing in a constant expression, as per 3.2.3:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless x is an object that satisfies the requirements for appearing in a constant expression
Therefore as it is not odr-used the odr-rule does not apply to it, 3.2.4:
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program
As for the two different definitions of widget
, and whether they are sufficiently similar according to 3.2.6. The answer is yes because N3485 3.2.6:
a name can refer to a const object [yes, constexpr is const] with internal [constexpr is internal] or no linkage if the object has the same literal type [yes, both int] in all definitions of D, and the object is initialized with a constant expression [yes, 10], and the value (but not the address) of the object is used, and the object has the same value [yes, 10] in all definitions of D
So even though the name foo
is referring to two different entities in the two different TUs, these two entities satisfy the given requirements.
(In practice it works because the compiler will layout the two classes identically, therefore the generated code from the two TUs will be compatible.)
回答3:
I don't get your interpretation. The quoted text applies to the quoted example properly. As long as you include it like that in all TUs. To violate ODR you must break something of the text:
constexpr double foo = 10; // type
constexpr int foo = ret_10(); // non-const init
constexpr int foo = 42; // value
And to break ODR-usage I think you must take its address.
来源:https://stackoverflow.com/questions/17139005/constexpr-and-odr