10

There is a post that deals with parentheses or not after the type name when using new. But what about this:

If 'Test' is an ordinary class, is there any difference between:

Test* test = new Test();
// and
Test* test = new Test{};

Furthermore, suppose Test2 has a constructor for an argument of type Value, is it always equivalent to write:

Value v;
Test2 *test2 = new Test(v);
// and
Test2 *test2 = new Test{v};
Community
  • 1
  • 1
user1978011
  • 3,419
  • 25
  • 38

3 Answers3

10

There might be difference in contexts involving std::initializer_list<>, e.g.:

Case 1 - () and {}

#include <initializer_list>
#include <iostream>
using namespace std;

struct Test2 {
    Test2(initializer_list<int> l) {}
};

int main() {
    Test2* test3 = new Test2(); // compile error: no default ctor
    Test2* test4 = new Test2{}; // calls initializer_list ctor
}

Case 2: (v) and {v}

struct Value {
};

struct Test3 {
    Test3(Value v) {}
    Test3(initializer_list<Value> v) {}
};

int main() {
    Value v;
    Test3* test5 = new Test3(v); // calls Test3(Value)
    Test3* test6 = new Test3{v}; // calls Test3(initializer_list<Value>)
}

As stated by Meyers and others there is also a huge difference when using STL:

    using Vec = std::vector<int>;
    Vec* v1 = new Vec(10); // vector of size 10 holding 10 zeroes
    Vec* v2 = new Vec{10}; // vector of size 1 holding int 10

and it is not restricted to std::vector only

In this case there's no difference though (and initializer_list ctor is ignored)

#include <initializer_list>
#include <iostream>
using namespace std;

struct Test {
    Test() {}
    Test(initializer_list<int> l) {}
};

int main() {
    Test* test1 = new Test(); // calls default ctor
    Test* test2 = new Test{}; // same, calls default ctor
}

There is also a well-known difference in this case

void f() {
    Test test{};
    Test test2();
}

where test is default-initialized object of type Test and test2 is a function declaration.

TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52
Peter K
  • 1,787
  • 13
  • 15
6

No!

A new-initializer may take the following forms:

new-initializer:
   ( expression-listopt)
   braced-init-list

And:

[C++11: 5.3.4/15]: A new-expression that creates an object of type T initializes that object as follows:

  • If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed, the object has indeterminate value.
  • Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-initialization.

And:

[C++11: 8.5/15]: The initialization that occurs in the forms

T x(a);
T x{a};

as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.

And:

[C++11: 8.5/16]: The semantics of initializers are as follows. [..]

  • If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
  • [..]
  • If the initializer is (), the object is value-initialized.
  • [..]
  • If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
  • [..]

So, you see, in this case (and a few others), the two are defined to both be direct-initialisation, but further rules mean that different things can occur depending on whether you use () or {} and whether the initialiser is empty.

Considering the rules for list-initialisation, which I shan't reproduce here, the two initialisers have essentially the same effect if T has no constructor taking an std::initializer_list<>.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
5

The general answer is no. The use of a braced-init-list as the initializer will first try to resolve to the constructor that takes an std::initializer_list. As an illustration:

#include <iostream>
#include <vector>
int main() {
  auto p = new std::vector<int>{1};
  auto q = new std::vector<int>(1);
  std::cout << p->at(0) << '\n';
  std::cout << q->at(0) << '\n';
}

Note the semantics of list-initialization (Quoted from cppreference):

  • All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list
  • If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all)
Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • i think they are different in the first case too – sp2danny May 03 '15 at 13:03
  • @sp2danny The semantics of list-initialization is really complicated. Different versions of C++ also have different statements about it. Anyway, I decide to leave the answer as it is now, since it is the most relevant part in daily programming, which people should get familiar with. But an edit to this answer to make it more accurate is welcome, also. – Lingxi May 03 '15 at 13:24