9

Assume I want to define two variables of class {Type}. The constructor takes 1 argument. Are the following two ways completely equivalent (compile to the same object code)?

Type a(arg), b(arg);

and

Type a(arg);
Type b(arg);

This question emerges after I read a page talking about exception safety --- http://www.gotw.ca/gotw/056.htm There is a guideline "Perform every resource allocation (e.g., new) in its own code statement which immediately gives the new resource to a manager object." It gives an example: The following snippet is safe

auto_ptr<T> t1( new T );
auto_ptr<T> t2( new T );
f( t1, t2 );

But the line below is not safe

f( auto_ptr<T>( new T ), auto_ptr<T>( new T ) );

So, how about

auto_ptr<T> t1( new T ), t2( new T );
f( t1, t2 );

I've looked up the document of C++ language standard, but found nothing specifying this issue.

To muddy the water, how about

shared_ptr<T> t1( new T );
shared_ptr<T> t2( t1 );

and

shared_ptr<T> t1( new T ), t2( t1 );
updogliu
  • 6,066
  • 7
  • 37
  • 50
  • 3
    Another way this question can be stated: Is the comma between declarations in a multi-declaration statement a sequence point? Good question. I'd never thought about it. – Benjamin Lindley Dec 07 '11 at 02:10
  • That's what I was wondering, too. It's not (as far as I can see) but as @Jon mentioned, the initializer expression itself is. – smparkes Dec 07 '11 at 02:25

3 Answers3

7

Yes, they are equivalent. See C++11, 8/3:

Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself97.

Footnote 97 is fairly lengthy, it says that T D1, D2, ... Dn; is "usually" equivalent to T D1; T D2; ... T Dn;, with the exceptions being cases where the meaning of T in subsequent declarations is affected by which one you do. The two examples given:

struct S {};
S S, T; // is not equivalent to:
S S; S T;

auto i = 1, j = 2.0; // is not equivalent to:
auto i = 1; auto j = 2.0;

But in both cases, one of the alternatives is an error, so this isn't a difference that'll result in you having to fire up the debugger.

Anyway yes, auto_ptr<T> t1( new T ), t2( new T ); is just as safe as auto_ptr<T> t1( new T ); auto_ptr<T> t2( new T );

Note that there is no guarantee that equivalent source compiles to the same object code, only that it has the same meaning. In your example, object code that contains debugging annotations would reasonably differ in terms of source line numbers, but the compiler might also introduce unexpected differences for some obscure reason or for no good reason at all.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
2

The compiler is free to evaluate both new T before it constructs the auto_ptrs while producing the parameters for the function call. If an exception is thrown somewhere in the middle of that sequence, you could get memory leaks.

As for what happens in a variable declaration check this previous question: Is the comma in a variable list a sequence point?

Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • I'm not sure about that. In "shared_ptr t1( new T ), t2( t1 );" t2 is definitly constructed after t1, isn't it? So why in "shared_ptr t1( new T ), t2( new T )" the order of construction is not fixed? – updogliu Dec 07 '11 at 02:12
  • 1
    @updogliu: In both cases there is a sequence point between the two initialization expressions, hence the order of construction is fixed. – Jon Dec 07 '11 at 02:15
  • Added some emphasis to prevent other readers from misreading the answer as I did. – Cheers and hth. - Alf Dec 07 '11 at 02:20
  • @Alf P. Steinbach: thanks. Doesn't look like it's going to save this answer from obscurity though. – Mark Ransom Dec 07 '11 at 02:29
2

Yes, by definition: http://www.open-std.org/jtc1/sc22/wg21/docs/wp/html/oct97/decl.html

Each init-declarator in a declaration is analyzed separately as if it was in a
declaration by itself.
smparkes
  • 13,807
  • 4
  • 36
  • 61