10

As in the question: let's say I have a small piece of code like this:

  #include <iostream>
  using namespace std;

  struct foo {
      int a; 
      foo() : a(12) {};
  };  

  int
  main()
  {
      volatile foo x;
      return 0;
  }   

compiled with g++ -g -O2 it turns out, that x initialization is optimized away.

That one however:

  #include <iostream>
  using namespace std;

  struct foo {
      volatile int a; 
      foo() : a(12) {};
  };  

  int
  main()
  {
      volatile foo x;
      return 0;
  }   

calls the constructor.

If I try to use the variables inside the code, (ie. cout << foo.a << endl;) the assembly output is equivalent in both cases.

Do I get the following right, that:

In the first case, there's no access to the struct at all, so it gets optimized away completely.

In the second one, struct's field is indicated as the one possible to change also during the construction and for that reason foo() is called no matter what.

Added: I've tried fiddling with the above code: calling things like while(foo.a--); works as expected, it actually happens instead of being deleted/replaced by result during optimization, thus it seems the volatile is actually inherited, yet the ctor behaves in this strange (or at least unexpected at first) way.

EDIT number 2:

I checked it with clang and MSVC and it behaves the same way as in gcc.

alagner
  • 3,448
  • 1
  • 13
  • 25
  • Looks like a bug to me. "**[basic.type.qualifier]/(1.2)** A *volatile* object is an object of type `volatile T`, a subobject of such an object..." So, in both examples, `foo.a` is a volatile object, and operations on it are observable side effects and should not be optimized away. – Igor Tandetnik Feb 08 '16 at 04:45
  • @IgorTandetnik I never really understood how volatile automatic variables could be observable. Accepting that it's a compiler bug, it's hard to see how it could possibly break anything in practice (and also hard to see how a compiler is supposed to cope with this possibility, the optimizer *should* be able to optimize the code for `foo`'s constructor without having to optimize separately for cases where a `foo` was instantiated as volatile.) – M.M Feb 08 '16 at 08:50

1 Answers1

2

My understanding is as follows (and I am not sure about it):

In C++ volatile keyword forces compiler not to optimize seemingly redundant loads and stores to memory, i.e. if you have such example code:

int x = 5;
x = 6;

It will not be changed to:

int x = 6;

This is because x could be pointing to some address in memory, that is read by others, while you don't read it really in your program (imagine that you are sending some configuration over USART to microcontroller by writing to certain memory address, and the microcontroller reads it's configuration from that address - if compiler was to optimize writes to this memory, then whole program would malfunction).

Another thing that one must remember is that when instance of a class is declared with volatile specifier, then its members inherit this specifier (as Igor Tandetnik pointed out in comment, referring to C++ Standard). But this is not the whole truth, because in order to get volatile behaviour, you would have to call member functions, that are marked as volatile - similar to marking member function as const (please see this: http://www.devx.com/tips/Tip/13671). Because AFAIK ctors/dtors cannot be marked with volatile keyword (as in Defining volatile class object), you would have to change your code a bit (perhaps invoking volatile member function from within ctor would do the thing, but this is only a guess).

Community
  • 1
  • 1
Piotr Smaroń
  • 446
  • 3
  • 11
  • Ctor cannot be v-specified. It seems like the case here, though I'd like to see it explained by the standard in a more explicit way, that's all. I've added some information to the question btw. – alagner Feb 08 '16 at 08:44
  • Yes, and I think I wrote that: "AFAIK ctors/dtors cannot be marked with volatile keyword". Or you mean sth else? – Piotr Smaroń Feb 08 '16 at 08:46