The following code outputs different results on various compilers:
Finally, how it should work according to C++ standard?
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
class Value;
using Array = std::vector<Value>;
class Value
{
public:
Value(const Array &)
{
}
Value(int)
{
}
};
void foo(Array const& a)
{
printf("%d\n", (int)a.size());
}
int main()
{
Array a1 = { 1, 2 };
foo(a1);
foo(Array{ a1 });
foo(Array({ a1 }));
}
P.S. The same issue reveals with json_spirit library from this article: http://www.codeproject.com/Articles/20027/JSON-Spirit-A-C-JSON-Parser-Generator-Implemented
Your program is ill-formed in C++11, as you have created a std::vector
type with an incomplete type as an argument. The value type of a vector
must be complete when you create the std::vector<T>
type.
As an ill-formed program (and I'm not aware of a requirement for a diagnostic), any and all behavior is legal under the standard.
The requirement that vector
's T
be a complete type is probably over-specified (there is really no convincing reason to have that requirement), but it exists. Asking the same question in C++1z will lead to a different answer, as that requirement was relaxed.
Ignoring that issue, philosophically:
Array a1 = { 1, 2 };
this should generate a std::vector
with two elements.
foo(a1);
This should pass a1
to foo
, as the types match exactly.
foo(Array{ a1 });
Here we have {}
. My rule of thumb with {}
is "if it is non-empty, and it can match an initializer_list
constructor, it must".
As a1
can be converted to a Value
, Array{ a1 }
is an array of one Value
.
foo(Array({ a1 }));
Here we look at an argument of the constructor Array
that can be called with { a1 }
. Both the std::initalizer_list<Value>
and the Array&&
constructors can be.
Which is called should not matter: The Array&&
constructor in turn sends the {a1}
to the std::initalizer_list<Value>
constructor.
So my opinion is that it should print 211
. This is, however, merely an opinion on what the could ought to do, not an analysis of what the standard says it should do, as the C++11 standard quite clearly states your program is ill-formed.
On the other hand, hiding the copy constructor seems rude.
On the final hand, you did write a simply insane type. Insane behavior is to be expected.
A more practical concern might be when the Value
type has a template
constructor that happens to also match a std::vector<Value>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With