【问题标题】:Why doesn't the compiler warn against ODR violations in the same translation unit为什么编译器不对同一翻译单元中的 ODR 违规发出警告
【发布时间】:2017-08-28 15:10:55
【问题描述】:

在同一个翻译单元中,ODR 问题很容易诊断。那么为什么编译器不会针对同一翻译单元中的 ODR 违规发出警告呢?

例如,在以下代码https://wandbox.org/permlink/I0iyGdyw9ynRgny6(在下面重现)中,检测是否已定义std::tuple_size 时存在 ODR 冲突。此外,当您取消注释 threefour 的定义时,未定义的行为很明显。程序的输出发生变化。

只是想了解为什么 ODR 违规行为如此难以捕捉/诊断/可怕。


#include <cstdint>
#include <cstddef>
#include <tuple>
#include <iostream>

class Something {
public:
    int a;
};

namespace {
template <typename IncompleteType, typename = std::enable_if_t<true>>
struct DetermineComplete {
    static constexpr const bool value = false;
};

template <typename IncompleteType>
struct DetermineComplete<
        IncompleteType,
        std::enable_if_t<sizeof(IncompleteType) == sizeof(IncompleteType)>> {
    static constexpr const bool value = true;
};

template <std::size_t X, typename T>
class IsTupleSizeDefined {
public:
    static constexpr std::size_t value =
        DetermineComplete<std::tuple_size<T>>::value;
};
} // namespace <anonymous>

namespace std {
template <>
class tuple_size<Something>;
} // namespace std

constexpr auto one = IsTupleSizeDefined<1, Something>{};
// constexpr auto three = IsTupleSizeDefined<1, Something>::value;

namespace std {
template <>
class tuple_size<Something> : std::integral_constant<int, 1> {};
} // namespace std

constexpr auto two = IsTupleSizeDefined<1, Something>{};
// constexpr auto four = IsTupleSizeDefined<1, Something>::value;

int main() {
    std::cout << decltype(one)::value << std::endl;
    std::cout << decltype(two)::value << std::endl;
}

【问题讨论】:

  • 它不必根据标准诊断这些错误(即使它可以检测到这些错误也可以)。
  • @Jarod42 对,但为什么呢?

标签: c++ templates c++17 one-definition-rule


【解决方案1】:

为了使模板编译速度更快,编译器会记住它们。

因为 ODR 保证模板的全名及其参数完全定义了它的含义,一旦你实例化一个模板并生成“它是什么”,你可以将一个表从它的名称(所有参数命名)存储到“它是什么”。

下次您将模板传递给它的参数时,它不会再次尝试实例化它,而是在记忆表中查找它。如果找到,它会跳过所有这些工作。

为了做你想做的事,编译器必须放弃这个优化并重新实例化模板每次你传递它的参数。这可能会导致构建时间大幅减慢。

在您的玩具代码中,也许不是,但在实际项目中,您可能拥有数千个独特的模板和数十亿个模板实例化,记忆化可以将模板实例化时间减少一百万倍。

【讨论】:

  • 速度是这里唯一的因素吗?
  • @Curious 好吧,我可以编写由于模板实例化递归限制而无法在没有记忆的情况下编译的代码,但在记忆的情况下编译得很好。并在 O(n^2) 中编译,无需记忆和 O(n)。我不保证没有其他原因,包括“一般来说解决它很困难,即使你的具体情况很容易,为什么要在简单的情况下解决它,而解决它的成本很高。 ”。我只是提供了一个充分的理由,真的。
猜你喜欢
  • 1970-01-01
  • 2011-01-28
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-06
  • 1970-01-01
相关资源
最近更新 更多