cv-qualified struct 的成员不是类似的 cv-qualified

cv-qualified struct's member is not similarly cv-qualified

根据这个answer,下面的代码应该没有错误编译:

#include <type_traits>

namespace
{

struct A { int i; };

volatile A a{};
static_assert(std::is_volatile< decltype(a) >{});
static_assert(std::is_volatile< decltype(a.i) >{});

}

但是有一个硬错误:

main.cpp:10:1: error: static_assert failed
static_assert(std::is_volatile< decltype(a.i) >{});
^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

Live example with clang 3.6.0.

这是一个 clang bug 还是我遗漏了一些重要的东西?

附加:

#include <type_traits>

namespace
{

struct A { int i; };

const A a{};
static_assert(std::is_const< decltype(a) >{});
static_assert(std::is_const< decltype(a.i) >{});

}

后一个代码片段的行为完全相同。

附加:

static_assert(std::is_volatile< std::remove_pointer_t< decltype(&a.i) > >{});

不会导致错误。

这是正确的行为;当前 decltype 并没有测试您认为它测试的内容。如果你想测试 volatile-ness,你必须改变你的 decltype

可以复制一个值并删除 volatile-ness。但是引用必须是 volatile reference-to-volatile.

int foo() {
    int i = a.i;            // volatile not needed
    volatile int &i = a.i;  // volatile needed
}

decltype a.i 视为右值,而不是左值, 执行 decltype 默认执行的操作,并且正在执行"naive" 文本分析和报告 A::i 的类型,如源代码中所写;因此,decltype(a.i)int.

decltype 中的表达式周围放置 () 会改变它的行为(通常以好的方式,对于这类问题),在适当的时候给我们一个左值。因此,decltype(( a.i ))volatile int &.

现在,您可能认为 is_volatile< volatile int & >::value 是正确的。但事实并非如此。我们需要先删除引用,然后才能测试 volatile-ness.

static_assert(std::is_volatile< std::remove_reference_t<decltype((a.i))> >{});

最后,根据 is_volatilevolatile int & 不是易变的,您可能会感到惊讶。但我想这是因为引用真的非常像指针——引用中的 'pointer' 不是易变的,即使指向的对象是易变的。无论如何,这就是我的理论。 const int&也不满足is_const.