std::piecewise_construct
, defined in <utility>, has internal linkage since it is declared constexpr
. I wonder if use of std::piecewise_construct
in a header can violate ODR. For example:
a.hpp
#include <utility>
#include <tuple>
struct point
{
point(int x, int y)
: x(x), y(y)
{}
int x, y;
};
inline std::pair<point, point> f(int x1, int y1, int x2, int y2)
{
return {
std::piecewise_construct,
std::forward_as_tuple(x1, y1), std::forward_as_tuple(x2, y2)
};
}
translation unit 1
#include "a.hpp"
translation unit 2
#include "a.hpp"
The std::piecewise_construct
in f
in TU 1 refers to a different object than that in f
in TU 2. I suspect f
violates ODR.
N3290 (probably ISO/IEC 14882:2011 also) says the following case is an exception of ODR, in 3.2/5:
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;
f
satisfies almost all the requirements, but "开发者_开发知识库the value (but not the address) of the object is used" seems ambiguous to me. It's true that std::piecewise_construct_t
has no state, but a call of the piecewise constructor of std::pair
involves a call of the implicit-declared copy constructor of std::piecewise_construct_t
, whose argument is const std::piecewise_construct_t &
. The address is "used", isn't it?
I'm very puzzled.
Reference: http://lists.boost.org/Archives/boost/2007/06/123353.php
It appears that you already have your answer in that boost mailing list posting. Yes, in my opinion it is undefined behavior or at least not sufficiently clear defined behavior.
See this usenet discussion for the same matter being discussed.
IMHO there is no conflict under the ODR.
An unnamed namespace has the same effect as marking things for internal linkage (static). This does indeed mean that every TU uses his own unique definitions for such types/functions.
The way I look at them, how the placeholders (::::_1 and competing flavours) work, is not by instantiation so much as by compiletime type inference:
_1, _2 etc. are merely placeholders, and they don't really need to be compatible (values don't need to be passed from one TU to another, they are passed as type inferred parameters only and as such their actual type is inferred to be have the identity
from the current TU).
IOW: You can easily define your own placeholders by specializing some traits, and they should still work like a charm.
namespace boost
{
template<int I> struct is_placeholder<
my_funny_own_placeholder_no_ODR_involved<I> >
{
enum _vt { value = I };
};
}
I suppose the same logic could hold for piecewise_construction (but I haven't looked at that much).
精彩评论