问题
The draft book Effective C++11 by Scott Meyers states:
Distinguish () and {} when creating objects
What's the difference between Object obj(args...)
and Object obj{args...}
? and why Scott says so.
Update:
The question How to use C++11 uniform initialization syntax? asks for HOW, and this question asks for WHY.
Update2:
I find the following link is helpful and completely answers this question:
https://softwareengineering.stackexchange.com/questions/133688/is-c11-uniform-initialization-a-replacement-for-the-old-style-syntax
回答1:
What's the difference between
Object obj(args...)
andObject obj{args...}
?
The first is direct-initialization while the second is direct-list-initialization. This is mentioned in two different sections:
§8.5/16 [dcl.init]
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 §8.5.4/1 [dcl.init.list]
List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.
There are a few differences between the two:
If the type being constructed has a constructor that takes an
initializer_list
argument, direct-list-initialization will always favor that constructor. Other constructors will only be considered if theinitializer_list
constructor is not viable. §13.3.1.7/1 [over.match.list]direct-list-initialization does not allow narrowing conversions within the argument list. §8.5.4/3 [dcl.init.list]
If the type being initialized is an aggregate, direct-list-initialization will perform aggregate initialization. §8.5.4/3 [dcl.init.list]
The order of evaluation of the elements of a braced-init-list is from left to right. §8.5.4/4 [dcl.init.list]
You can avoid the most vexing parse by using direct-list-initialization
struct foo{};
struct bar
{
bar(foo const&) {}
};
bar b1(foo()); // most vexing parse
bar b2(foo{}); // all 3 of the following construct objects of type bar
bar b3{foo()};
bar b4{foo{}};
回答2:
The behavior of Object obj(args...)
and Object{args...}
depends on the constructors defined in Object
.
Take the following example:
#include <iostream>
#include <initializer_list>
struct A
{
A(int a, int b) {std::cout << "Came to A::A()\n";}
};
struct B
{
B(int a, int b) {std::cout << "Came to B::B(int, int)\n";}
B(std::initializer_list<int> in) {std::cout << "Came to B::B(std::initializer_list<int>)\n";}
};
int main()
{
A a1(10, 20); // Resolves to A(int, int)
A a2{10, 20}; // Resolves to A(int, int)
A a3{30}; // Does not resolve to anything. It's a compiler error.
B b1(10, 20); // Resolves to B(int, int)
B b2{10, 20}; // Resolves to B(std::initializer_list<int> )
B b3{30}; // Resolves to B(std::initializer_list<int> )
}
回答3:
What's the difference between Object obj(args...) and Object obj{args...}?
{args...} will prefer a constructor with an initializer_list over other legal candidates.
std::vector<int> v(10); // vector of size 10
std::vector<int> v{10}; // vector initialized with a single element, (int) 10
On the other hand, you give up implicit narrowing.
std::vector<int> v(10.5); // vector of size 10
std::vector<int> v{10.5}; // illegal - no compile
std::vector<float> v{10.5}; // vector initialized with a single element, (float) 10.5
回答4:
What's the difference between Object obj(args...) and Object obj{args...}? and why Scott says so.
The difference is that in the former case, the order of evaluation of arguments is unsequenced (i.e unspecified) but in the latter case, the order is left-to-right (i.e in which they appear).
The following text from $5.2.2/8 [expr.call] (n3690) deals with Object(args...)
form:
The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered (see 1.9).
And the text from $8.5.4/4 [dcl.init.list] (n3690) deals with Object{args...}
form:
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.[ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. — end note ]
Well which means this:
int f() { static int i = 10; return ++i; } //increment the static int!
Object obj(f(), f()); //is it obj(11,12) or obj(12,11)? Unspecified.
Object obj{f(), f()}; //it is obj(11,12). Guaranteed.
Note that GCC (4.7.0 and 4.7.2) have a bug because of which {} form doesn't work the way it should. I'm not sure if it is fixed in the current version.
Hope that helps.
来源:https://stackoverflow.com/questions/24720028/whats-the-difference-between-object-objargs-and-object-objargs