19

I am trying to disallow a specific operation on volatile types. To accomplish this I am trying to use std::is_volatile, but the code below is compiling without errors, which is not what I want.

Why is is_volatile::value false in the case below?

#include <type_traits>

template<typename T>
inline void DoStuff(T val) {
    static_assert(!std::is_volatile<T>::value, "No volatile types plz");
    //...
}

int main() {
    volatile char sometext[261];
    DoStuff(sometext);
}
MathuSum Mut
  • 2,765
  • 3
  • 27
  • 59
  • That's just how template argument deduction works -- qualifiers are not part of deduced by-value parameter types. This is essentially the same as: `volatile int src = ... ; int n = src;`. Note how `n` knows nothing about the qualification of `src`. In other words, lvalue-to-rvalue conversion discards qualifiers. – Kerrek SB Oct 05 '17 at 09:38
  • @KerrekSB maybe you overlooked that an array is given as argument – M.M Oct 05 '17 at 10:06
  • @M.M: No, that merely complicates the details a bit, but the core issue remains. – Kerrek SB Oct 05 '17 at 10:53
  • 2
    @KerrekSB: The `volatile` qualifier in this case is deduced, but that is because it's not a top-level qualifier. Your example simplifies the question too far: with just a single level, there's no distinction between top-level qualifiers and qualifiers at other levels. And the crux of the answer (`remove_pointer`) is that you look at the next level, i.e. beyond the top level. – MSalters Oct 05 '17 at 12:45
  • @MSalters: Yes, that's true. – Kerrek SB Oct 05 '17 at 14:29

3 Answers3

28

The problem is that T isn't a volatile type at all. It's volatile char*. Wait a minute, you say, I see volatile right there. True, but consider this: char* volatile is a volatile type. volatile char* isn't. It's a non-volatile pointer to a volatile char array.

Solution: std::is_volatile<typename std::remove_pointer<T>::type>

MSalters
  • 173,980
  • 10
  • 155
  • 350
9

When trying to pass the array by value, it decays into a pointer to its first element.

This means that val is actually a int volatile *. As such, it points to a volatile int, but is not itself volatile. Hence, std::is_volatile returns false.

You may try taking in the array by reference, or using std::remove_pointer.

Quentin
  • 62,093
  • 7
  • 131
  • 191
1

Because the functions accepts its argument by value, the cv-qualification of the original argument is lost.

Accept it by reference:

void DoStuff(T& val)
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Considering the example argument is an array, `T* val` might make more sense (but we're missing context) – MSalters Oct 05 '17 at 15:06