Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

According to this answer, the following code should be compiled without error:

#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) >{});

}

but there is a hard error:

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.

Is it a clang bug or am I missing something substantial?

ADDITIONAL:

#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) >{});

}

Exactly the same behaviour for the latter code snippet.

ADDITIONAL:

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

not cause an error.

like image 504
Tomilov Anatoliy Avatar asked Jul 31 '15 09:07

Tomilov Anatoliy


1 Answers

This is the correct behaviour; the current decltype isn't testing what you think it tests. You have to change your decltype if you want to test for volatile-ness.

It is possible to copy a value out and drop volatile-ness. But a reference must be volatile reference-to-volatile.

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

The decltype is treating a.i as an rvalue, not an lvalue, doing what decltype does by default and is doing a "naive" text analysis and reporting the type of A::i as written in the source code; therefore, decltype(a.i) is int.

Putting () around an expression in decltype changes its behaviour (in good ways usually, for this kind of question), giving us an lvalue when appropriate. Therefore, decltype(( a.i )) is volatile int &.

Now, you might expect that is_volatile< volatile int & >::value would be true. But it's not. We need to remove the reference before we can test for the volatile-ness.

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

Finally, you might be surprised that volatile int & isn't volatile according to is_volatile. But I guess it's because references are really very like pointers - and the 'pointer' inside a reference isn't volatile, even if the object pointed to is volatile. That's my theory anyway. const int& also does not satisfy is_const.

like image 138
Aaron McDaid Avatar answered Nov 10 '22 14:11

Aaron McDaid