在同一个翻译单元中,ODR问题很容易诊断。那么为什么编译器不会在同一个翻译单元中警告ODR违规?
例如,在以下代码https://wandbox.org/permlink/I0iyGdyw9ynRgny6(下面转载)中,存在ODR违规,检测是否已定义std::tuple_size
。当您取消注释three
和four
的定义时,未定义的行为会更加明显。程序的输出发生了变化。
试图理解为什么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;
}
答案 0 :(得分:6)
为了快速进行模板编译,编译器会记住它们。
因为ODR保证模板的全名及其参数完全定义它的含义,所以一旦实例化模板并生成“它是什么”,就可以从其名称(所有参数都命名)中存储一个表到“它是什么”。
下次你传递模板的参数,而不是尝试再次实例化它,它会在memoization表中查找它。如果找到,它会跳过所有这些工作。
为了做你想做的事,编译器必须放弃这个优化,并在每次传递参数时重新实例化模板。这可能会导致构建时间大幅减缓。
在您的玩具代码中,可能没有,但在真实项目中,您可以拥有数千个独特模板和数十亿个模板实例,这些内存可以将模板实例化时间缩短一百万倍。