1 <!-- subject: Most vexing parse -->
2 <!-- date: 2021-04-25 16:22:02 -->
3 <!-- tags: c++, mvp, most vexing parse -->
4 <!-- categories: Misc, Techblog -->
6 <p>Here’s a puzzle: What does the following C++ code output:
14 std::printf(
"Hell%s,", std::string(n, 'o').c_str());
17 std::printf(
"%s",
" world");
21 static constexpr double pi =
3.141592653589793238;
25 Foo bar(unsigned(pi));
31 <p>If your reaction is ‘What do you mean? It’s obvious!’ than you’ve answered
32 incorrectly. But if you’ve exclaimed ‘This is implementation-defined because
33 there’s no terminating new-line character in text stream!’ than
34 congratulations on knowing this fact; that’s still the wrong answer though.
35 The correct reaction is a sigh followed by a lament at C++ syntax.
37 <p>The
<code>Foo foo();
</code> line is a
<em>function declaration
</em> and has
38 no effect on the behaviour of the code. Perhaps more confusingly,
<code>Foo
39 bar(unsigned(pi);
</code> is
<em>also
</em> just a function declaration — this
40 time of a function with a single unsigned argument — and doesn’t affect output
41 of the program. In the end, the program outputs nothing and returns with
44 <p>This is an instance of the infamous
<dfn>most vexing parse
</dfn> which is
45 a syntax ambiguity where a piece of code could be interpreted as a function
46 declaration or creation of an object. In those situations C++ mandates the
47 syntax to be parsed as a function declaration.
50 <h2>Removing the ambiguity
</h2>
52 <p>There are a few possible ways to force the compiler to treat the lines as an
53 object definitions. In case of
<code>foo
</code>, the simplest (and arguably
54 best) is to remove the parenthesis all together. In case
55 of
<code>bar
</code>, an explicit cast, that is
56 using
<code>static_cast
<unsigned
>(pi)
</code>, would resolve the issue.
58 <p>C++
11 offers another option in the form of aggregate initialisation (also
59 known as brace initialisation). Simply replace parenthesis around the
60 supposed constructor arguments with braces and the code will behave as
64 static constexpr double pi =
3.141592653589793238;
68 Foo bar{unsigned(pi)};
72 <p>But before you go on a rampage and start replacing all parenthesis with
76 <h2>Here’s another puzzle
</h2>
78 <p>What does the following C++ code output:
81 #include
<iostream
>
85 typedef std::vector
<int
> Vector;
92 std::cout << a.size() << ' ' << a[
0] <<
", "
93 << b.size() << ' ' << b[
0] <<
", "
94 << c.size() << ' ' << c[
0] <<
", "
95 << d.size() << ' ' << d[
0] <<
", "
96 << e.size() << ' ' << e[
0] <<
", "
97 << f.size() << ' ' << f[
0] << '\n';
101 <p>In constructor call the parenthesis can be replaced by braces so in the code
102 above
<code>a
</code> is the same as
<code>b
</code> and
<code>d
</code> is the
103 same as
<code>e
</code>, right? Not so fast.
106 <li>The definition of variable
<code>a
</code> is equivalent to
<code>Vector
107 a(
6,
0);
</code>. Nothing surprising here. This constructs a six-element
108 vector with all elements initialised to zero.
109 <li>However, the definition of variable
<code>b
</code> is different.
110 Because
<code>std::vector
</code> has a constructor which
111 takes
<code>std::initializer_list
</code> as an argument, when braces are
112 used that constructor is used. As such, the
<code>b
</code> variable is in
113 fact equivalent to
<code>c
</code>. They are both one-element vectors
114 holding the value six.
115 <li>The definition of variable
<code>d
</code> is analogous to
<code>a
</code>
116 and results in a six-element vector with all elements set to nine. Again,
118 <li>Finally, the definitions of variables
<code>e
</code> and
<code>f
</code>
119 are equivalent and — just like with
<code>b
</code> and
<code>c
</code> — call
120 constructor with an initialiser list resulting in a two-argument vector
121 holding values six and nine.
124 <p>The output of the program is therefore
<code>6 0,
1 6,
1 6,
6 9,
2 6,
2
130 <p>The most vexing parse is not a new issue — it fact it’s now two decades since
131 Scott Meyers coined the term in his Effective STL book — and I certainly won’t
132 make any new revelations about it. It is unfortunate that in attempt to solve
133 it the committee decided it was a good idea to allow braces themselves to be
134 a shorthand for calling constructor with initialiser list. This decision
135 means that adding a constructor which takes
<code>std::initializer_list
</code>
136 is now an API-breaking change. How? Let’s consider the following code:
139 #include
<iostream
>
143 DynArr(size_t size) : ptr(std::make_unique
<int[]
>(size)), sz(size) {}
146 DynArr(std::initializer_list
<int
> lst) : DynArr(lst.size()) {
147 std::copy(lst.begin(), lst.end(), ptr.get());
151 size_t size() const { return sz; }
152 int *data() { return ptr.get(); }
153 int
&operator[](size_t offset) { return ptr[offset]; }
154 int
&last() { return ptr[sz -
1]; }
157 const std::unique_ptr
<int[]
> ptr;
164 for (size_t i =
2; i
< arr.size(); ++i) {
165 arr[i] = arr[i -
2] + arr[i -
1];
167 std::cout
<< arr.last()
<< '\n';
171 <p>With only the
<code>DynArr(size_t)
</code> constructor present,
172 the
<code>DynArr arr{
5}
</code> definition allocates a five-element array and
173 the code calculates fifth number in the Fibonacci sequence. But if a later
174 revisions of the class introduce
175 the
<code>DynArr(std::initializer_list
<int
>)
</code> constructor, the
176 same line will invoke this new constructor and later cause a buffer overflow.
178 <p>Unfortunately (as often is the case when talking about C++) there are no
179 definite answers to the most vexing parse or the potential confusion with
180 aggregate initialisation. It’s remains one of those things a C++ programmer
184 <h2>Epilogue: A bonus puzzle
</h2>
186 <p>I’ll leave you, Dear Reader, with this puzzle. What does the following C++
195 Foo(unsigned n =
1) {
196 std::printf(
"Hell%s, world\n", std::string(n, 'o').c_str());
201 Foo bar(unsigned(M_PI));
205 <p>C and C++ are so much fun!