首页 > 解决方案 > 了解模板类型/值的重复评估

问题描述

我有以下代码,我不明白为什么只有在我没有在定义之前!has_size<Bar>::value注释掉完全相同的情况下最后一个评估为真static_assert( !has_size<Bar>::value, ...)Bar

#include <type_traits>

template <class, class = void> struct has_size : std::false_type {};
template <class T> struct has_size<
    T, typename std::enable_if<(sizeof(T) > 0)>::type> 
    : std::true_type
{};

// expected success
struct Foo {};
static_assert( has_size<Foo>::value, "Foo doesn't have size");

struct Bar; // declare bar

// if we comment out this line, the static_assert below struct Bar{} fails
static_assert( !has_size<Bar>::value, "Bar has a size");    

struct Bar {};  // define bar

// why is this true now, but false if I comment out the previous assertion?
static_assert( !has_size<Bar>::value, "Bar has a size");

我想稍后根据has_size<Bar>. msvc、gcc 和 clang 的行为相同。我试图弄清楚这是否是有意的和有据可查的行为,或者我是否正在通过这种行为徘徊在 UB 土地或其他一些灾难中。想法?

标签: c++c++11templatesstatic-assert

解决方案


您可以将类模板实例化视为“缓存”或“记忆”。更正式地说,类模板的每个翻译单元都有一个实例化点。

所以当你写:

struct Bar;
static_assert( !has_size<Bar>::value, "Bar has a size");   // #1
struct Bar {};
static_assert( !has_size<Bar>::value, "Bar has a size");   // #2

has_size<Bar>在 被实例化#1。这是它唯一的实例化点。在#2,我们不会“重做”那个计算——所以它仍然是错误的。如果我们从不同的翻译单元再次执行此操作,以给出不同答案的方式,那将是格式错误的(不需要诊断),但在这种情况下 - 这是一个格式正确的程序。

当您注释掉时#1,现在的实例化点has_size<Bar>变为#2. 并且在程序的那个点上,Bar完成了,has_size<Bar>现在也是true_type......所以静态断言触发了。


推荐阅读