No, these are not the same.
The difference between them is the same that applies for direct-initialization vs. copy-initialization, which is subtle but often very confusing.
§12.6.2 [class.base.init]:
The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 8.5 for direct-initialization. [...]
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
— if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
§8.5 [dcl.init]:
The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.
Initializing a non-static data member on a member-initializer-list follows the rules of direct-initialization, which doesn't create intermediate temporaries that need to be moved/copied (if compiled without a copy-elision), neither the type of the data member must be copyable/movable (even if the copy is elided). In addition, a direct-initialization introduces an explicit context, while a copy-initialization is non-explicit (if a constructor selected for the initialization is explicit
, the program won't compile).
In other words, the obj s = obj("value");
syntax won't compile if obj
is declared as:
struct obj
{
obj(std::string) {}
obj(const obj&) = delete;
};
or:
struct obj
{
obj(std::string) {}
explicit obj(const obj&) {}
};
As a more tangible example, while the below won't compile:
struct any
{
std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};
this one will:
struct any
{
std::atomic<int> a;
std::atomic<int> b{ 2 };
any() : a(1) {}
};
Which way is better (performance) ?
With a copy-elision enabled both have identical performance. With copy-elision disabled, there is an additional copy/move constructor call upon every instantiation when the copy-initialization syntax is used (that obj s = obj("value");
is one of).
Is there another way ?
The brace-or-equal-initializer syntax allows one to perform a direct-list-initialization as well:
class any {
public:
obj s{ "value" };
any() {}
};
Are there any other differences?
Some other differences that are worth mentioning are:
- Brace-or-equal-initializer must reside in a header file along with a class declaration.
- If both are combined, member-initializer-list takes priority over brace-or-equal-initializer (that is, brace-or-equal-initializer is ignored).
- (C++11 only, until C++14) A class that uses brace-or-equal-initializer violates constraints for an aggregate type.
- With the brace-or-equal-initializer syntax it's not possible to perform a direct-initialization other than a direct-list-initialization.