3
// OK!
volatile CString* a0;
CString* a1 = const_cast<CString *>(a0);

// error C2440: 'const_cast' : cannot convert from 'volatile CString' to 'CString'
volatile CString b0;
CString b1 = const_cast<CString>(b0);

I was wondering, why const_cast only work for pointer? How can I make the 2nd case to compile?

iammilind
  • 68,093
  • 33
  • 169
  • 336
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

2 Answers2

10

const_cast acts on pointers and references, to remove const and volatile qualifiers. It doesn't make sense to use it to cast to an object type, since then you would making a copy which need not have the same cv-qualifiers as the original anyway.

Your second example will compile if you cast to a non-volatile reference:

volatile CString b0;
CString & b1 = const_cast<CString &>(b0);

However, using that reference gives undefined behaviour, since the underlying object is itself volatile. You should only use const_cast to remove qualifications when you know that the underlying object does not have those qualifications (or in the case of removing const, when you know that the result won't be used to modify the object).

You can only get a copy if the object allows copying of volatile objects (perhaps with a copy constructor taking a reference-to-volatile or a volatile-qualified function or conversion operator). If CString doesn't provide any of these, then you can't safely copy a volatile object of that type.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • For `CString b1 = b0;` to compile, one needs copy-ctor of this form : `CString(CString volatile &)`. Right? – Nawaz Nov 18 '11 at 11:28
  • +1, but are you sure that removing a `volatile` and accessing is UB? For example what is UB is removing a const qualifier and **writing** to an object that is declared const, but just reading from it after removing the const qualifier is ok. Removing a `volatile` qualifier and access an object being sure that for example **no other threads are accessing it in that moment** (supposing this was the reason for `volatile`) I'd say that is also ok. – 6502 Nov 18 '11 at 11:32
  • 2
    @6502: C++11 7.1.6.1/6 says "If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program behavior is undefined." `volatile` has nothing to do with threads, except in some compilers with a non-standard extension that makes it also mean "atomic". It defines accesses to the object as visible side-effects of the program; removing the qualification can change those side-effects, and therefore change the program's behaviour in an undefined way. – Mike Seymour Nov 18 '11 at 11:38
  • @MikeSeymour: Thanks, I learned something new. I always thought that volatile was used to just inform the compiler to make no assumption about it being a regular object with read-write semantic (e.g. to avoid making it assume that after writing something in it the value obtained by reading would be the same) and to respect write and read order. I wouldn't have classified for example reading from a volatile object using a non-volatile pointer UB (but just not necessarily respecting access order for example). – 6502 Nov 18 '11 at 11:58
  • 2
    The moral of the story is that people who write classes usually make an effort to ensure that `const` instances of that class do something sensible, by providing `const` versions of non-mutating member functions, and taking arguments by `const MyClass &` where appropriate. They very rarely do anything similar for `volatile` instances. You might find that the only thing you can do with a `volatile` instance of many classes is destroy it, standard classes included. – Steve Jessop Nov 18 '11 at 12:09
0

because in the second case you are actually copying b0 and not referring to the original object

you need to do a reference in that case

const CString &b1 = b0;
AndersK
  • 35,813
  • 6
  • 60
  • 86