std::piecewise_construct
,在constexpr
以来具有内部链接。我想知道在头中使用std::piecewise_construct
是否会违反ODR。例如:
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)
};
}
翻译单元1
#include "a.hpp"
翻译单元2
#include "a.hpp"
图1中f
中的std::piecewise_construct
所指的对象与图2中f
所指的对象不同。我怀疑f
违反了ODR。
N3290(可能也是ISO/IEC 14882:2011)说以下情况是ODR的例外,在3.2/5中:
如果对象在D的所有定义中具有相同的文字类型,并且使用常量表达式(5.19)初始化对象,并且使用了对象的值(但不是地址),并且该对象在D的所有定义中具有相同的值,则名称可以引用具有内部链接或不具有链接的const对象;
f
几乎满足了所有的要求,但是"使用对象的值(而不是地址)"对我来说似乎模棱两可。的确,std::piecewise_construct_t
没有状态,但是对std::pair
的分段构造函数的调用涉及到对std::piecewise_construct_t
的隐式声明的复制构造函数的调用,其参数是const std::piecewise_construct_t &
。地址是used,对吧?
我很困惑。
参考:http://lists.boost.org/Archives/boost/2007/06/123353.php
看来你已经在那个boost邮件列表帖子中得到了答案。是的,在我看来,这是一种未定义的行为,或者至少是定义不够明确的行为。
请参阅此用户组讨论以了解正在讨论的相同问题。
我认为ODR下没有冲突。
未命名的命名空间与为内部链接(静态)标记事物具有相同的效果。这确实意味着每个TU对这些类型/函数使用自己独特的定义。
我看待它们的方式,占位符(::::_1和竞争版本)如何工作,不是通过实例化,而是通过编译时类型推断:
_1, _2等只是占位符,它们并不需要真正兼容(值不需要从一个TU传递到另一个TU,它们只作为类型推断参数传递,因此它们的实际类型被推断为具有当前TU的 identity
)。
ow:你可以很容易地定义你自己的占位符通过专门的一些特征,他们应该仍然像一个魅力。
namespace boost
{
template<int I> struct is_placeholder<
my_funny_own_placeholder_no_ODR_involved<I> >
{
enum _vt { value = I };
};
}
我想同样的逻辑可以适用于piecewise_construction(但我还没有看那么多)。