c – 为什么编译器不在同一个翻译单元中警告ODR违规
作者:互联网
在同一个翻译单元中,ODR问题很容易诊断.那么为什么编译器不会在同一个翻译单元中警告ODR违规?
例如,在下面的代码https://wandbox.org/permlink/I0iyGdyw9ynRgny6(下面再现)中,存在ODR违规,检测是否已定义std :: tuple_size.当你取消三和四的定义时,未定义的行为是显而易见的.程序的输出发生了变化.
只是试图理解为什么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;
}
解决方法:
为了快速进行模板编译,编译器会记住它们.
因为ODR保证模板的全名及其参数完全定义它的含义,所以一旦实例化模板并生成“它是什么”,就可以将一个表从其名称(所有参数都命名)存储到“它是什么”是”.
下次将模板传递给它的参数,而不是尝试再次实例化它时,它会在memoization表中查找它.如果找到,它会跳过所有这些工作.
为了做你想做的事,编译器必须放弃这个优化,并在每次传递参数时重新实例化模板.这可能会导致构建时间大幅减少.
在您的玩具代码中,也许不是,但在真实的项目中,您可以拥有数千个独特的模板和数十亿个模板实例,这些内存可以将模板实例化时间缩短一百万倍.
标签:one-definition-rule,c,templates,c17 来源: https://codeday.me/bug/20190823/1699224.html