【问题标题】:Doesn't std::piecewise_construct cause a ODR violation?std::piecewise_construct 不会导致 ODR 违规吗?
【发布时间】:2011-11-29 20:52:25
【问题描述】:

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"

TU 1 中f 中的std::piecewise_construct 与TU 2 中f 中的对象不同。我怀疑f 违反了ODR。

N3290(也可能是 ISO/IEC 14882:2011)在 3.2/5 中表示以下情况是 ODR 的例外:

如果对象在 D 的所有定义中具有相同的文字类型,并且该对象使用常量表达式(5.19)初始化,并且值(但不是使用对象的地址),并且该对象在D的所有定义中具有相同的值;

f 几乎满足所有要求,但是“使用对象的值(但不是地址)”对我来说似乎模棱两可。 std::piecewise_construct_t 确实没有状态,但调用std::pair 的分段构造函数涉及调用std::piecewise_construct_t 的隐式声明的复制构造函数,其参数为const std::piecewise_construct_t &amp;。地址被“使用”了,不是吗?

我很困惑。

参考:http://lists.boost.org/Archives/boost/2007/06/123353.php

【问题讨论】:

  • +1(我认为)对于 2 分钟后仍然让我毫无头绪的问题
  • 这不是和std::cout一样的问题吗?
  • @Kerrek std::cout 未定义多次。它只是在其各自的标题中声明。
  • std::coutextern 并定义一次。
  • 地址是否真的被使用了?注 84 表明它可能不会,因为 piecewise_construct_t 的复制 ctor 是微不足道的,因此 constexpr (7.1.5/4)

标签: c++ c++11 constexpr linkage one-definition-rule


【解决方案1】:

您似乎已经在该提升邮件列表发布中找到了答案。是的,在我看来,这是未定义的行为,或者至少没有足够明确的定义行为。

请参阅this usenet discussion 了解正在讨论的同一问题。

【讨论】:

  • 我也这么认为。顺便说一句,与 Boost 不同的是,标准库不需要只有标头。因此“piecewise_construct”应该被声明为:extern const piecewise_construct_t piecewise_construct;。我想知道为什么没有。
  • 我建议在 comp.std.c++ 或 comp.lang.c++.moderated 上询问。
  • 这在技术上是非法的,但实际上是无害的。
【解决方案2】:

恕我直言,ODR 下没有冲突。

未命名的命名空间与为内部链接(静态)标记事物具有相同的效果。 这确实意味着每个 TU 对此类类型/函数使用自己独特的定义。

我看待它们的方式,占位符(::::_1 和竞争风格)的工作方式,与其说是通过实例化,不如说是通过编译时类型推断:

_1、_2 等只是占位符,它们并不真正需要兼容(值不需要从一个 TU 传递到另一个 TU,它们作为 类型推断参数传递 ,因此它们的实际类型被推断为具有当前 TU 的 identity)。

IOW:您可以通过专门化某些特征来轻松定义自己的占位符,它们仍然应该像魅力一样发挥作用。

namespace boost
{
    template<int I> struct is_placeholder< 
           my_funny_own_placeholder_no_ODR_involved<I> >
    {
        enum _vt { value = I };
    };
}

我想对于 piecewise_construction 也可以采用同样的逻辑(但我没看过那么多)。

【讨论】:

  • “问题”是在考虑两个 TU 时,f 的各自定义是不同的,因为它们不引用相同的 std::piecewise_construct,并且使用的是地址,而不是值。因此,潜在的 ODR 违规是 f 在每个 TU 中可能不同。更多信息可在 Johannes 链接的讨论中找到。
  • @LucDanton:这是怎么回事?它们只是不同未命名命名空间中的不同功能?请注意,我主要基于关于 _1、_2... 占位符的链接讨论。我将阅读有关分段的链接讨论...稍后
  • f 不在未命名的命名空间中。如果是,你是对的,不会有问题。
  • @LucDanton:我明白了;在对该线程进行一些阅读时,它似乎专门处理传递对该类型实例的引用。这是个问题,是的,因为类型在技术上是不一样的(未命名的命名空间根据定义是唯一的)。嗯。有趣的。我以后一定会读到的
  • 问题不在于类型; std::piecewise_construct 在所有 TU 中都将具有 std::piecewise_construct_t 类型,同样const int internal = 42; 将始终具有 int 类型。但地址会有所不同。
猜你喜欢
  • 2016-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-03
  • 2018-07-17
  • 1970-01-01
相关资源
最近更新 更多