How should the following C ++ 11 code work?

The following code displays different results for different compilers:

  • 2,1,2 at visual studio 2013
  • 2,2,2 at visual studio 2015
  • 2,1,1 on GCC5 / C ++ 14
  • 2.2.2 on Clang 3.7 with Microsoft Codegen
  • 2,1,2 on Clang 3.6 / C ++ 11 under Ubuntu 14.04

Finally, how should it work in accordance with the 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 })); } 

PS The same problem is revealed in the json_spirit library from this article: http://www.codeproject.com/Articles/20027/JSON-Spirit-AC-JSON-Parser-Generator-Implemented

+6
source share
2 answers

Your program is poorly formed in C ++ 11, since you created the std::vector type with an incomplete type as an argument. The type of the vector value must be completed when the type std::vector<T> .

As an incorrect program (and I do not know the requirements for diagnostics), any and all behavior is legal by standard.

The requirement that vector T is a full type is probably overridden (there is no compelling reason for this), but it does exist. Asking the same question in C ++ 1z, you will get a different answer, as this requirement has been relaxed.


Ignoring this problem philosophically:

 Array a1 = { 1, 2 }; 

this should generate a std::vector with two elements.

 foo(a1); 

This should go a1 to foo , as the types match exactly.

 foo(Array{ a1 }); 

Here we have {} . My rule of thumb with {} is "if it is not empty, and it can match the initializer_list constructor, it should be."

Since a1 can be converted to Value , Array{ a1 } is an array of one Value .

 foo(Array({ a1 })); 

Here we look at the argument of the Array constructor, which can be called using { a1 } . Both the constructor std::initalizer_list<Value> , and Array&& can be.

What is called does not matter: the Array&& constructor, in turn, sends {a1} to the std::initalizer_list<Value> constructor.

Therefore, I believe that it should print 211 . This, however, is just an opinion of what could be done, and not an analysis of what the standard says it should do, since the C ++ 11 standard clearly states that your program is poorly formed.

Hiding the copy constructor, on the other hand, seems rude.

In your last hand you wrote just a crazy type. Expect crazy behavior.

A more practical problem might be that the Value type has a template constructor that also matches std::vector<Value> .

+5
source

In C ++ 11, the definition of compilers for matching initializers with fixed constructors that accept std::initializer_list is so strong that it prevails even if the best-match constructor std::initializer_list cannot be called (Scott Meyers).

 Array a1 = { 1, 2 }; // Call Initiliazer list constructor foo(a1); // print 2 foo(Array{ a1 }); // Call Initiliazer list constructor. Print 1 foo(Array({ a1 })); // Call Initiliazer list constructor. Print 1 

According to the standard, the result should be: 2,1,1

Yakk's answer mentions that the program is poorly formed in C ++ 11. But this post is very interesting.

0
source

All Articles