Why can't I initialize a reference in an initializer list with uniform initialization?
Yes, its a bug. This is something new and was voted in the working paper in February 2012 (link).
Nicol Bolas makes a good point in that gcc is actually the conforming compiler according to the FDIS approved C++11 standard because the changes to the working paper were made after that.
Initialization of const reference member in initializer list
GCC is correct in its interpretation of {}
. [dcl.init.list]/p3.8-9 (quoting N4296; earlier drafts has the same relative ordering of these two bullets):
List-initialization of an object or reference of type
T
is defined as
follows:
[7 inapplicable bullets omitted]
Otherwise, if
T
is a reference type, a prvalue temporary of the type referenced byT
is copy-list-initialized or
direct-list-initialized, depending on the kind of initialization for
the reference, and the reference is bound to that temporary. [ Note:
As usual, the binding will fail and the program is ill-formed if the
reference type is an lvalue reference to a non-const type. —end note
]- Otherwise, if the initializer list has no elements, the object is value-initialized.
List-initializing the reference hits bullet 3.8, causing the construction of a temporary. The value-initialization case, in 3.9, doesn't apply.
Value-initialization of a reference is ill-formed ([dcl.init]/p9):
A program that calls for default-initialization or
value-initialization of an entity of reference type is ill-formed.
However, as of N4296, per [class.base.init]/p8:
A temporary expression bound to a reference member in a
mem-initializer is ill-formed.
This was added as a result of CWG issue 1696, which is a DR (defect report) against C++14.
Pre-CWG1696, the standard provided that (N4140 [class.temporary]/p5.1):
A temporary bound to a reference member in a constructor’s
ctor-initializer (12.6.2) persists until the constructor exits.
which means that the reference will become dangling immediately after construction. This presumably motivated CWG1696's decision to disallow such bindings altogether.
Why doesn't this work? (brace-initialization of references)
The first thing to notice is that the initializer {1, "t"}
is using brace elision to initialize the sub-aggregate A.c
which means that the literal "t"
is taken to directly initialize the internal array that std::array
holds. In this case, this array would look like char data[12]
.
This reduces to say that we are initializing a reference to const A
with a brace-list containing an element that initializes a member array.
This will be somewhat equivalent to:
struct S {
char const data[2];
};
S const& s = {"t"}; // fail for gcc
GCC has already a bug report on that.
You've already provided a workaround in the comment section. Just initialize the reference as:
const A& ar = A{1, "t"}
Can't initialize an object in a member initialization list
The int
is coming from the fact that CTypedPtrMap
has a constructor that takes an int
argument that is defaulted to 10.
The real problem that you're running into is that the m_calcMap
reference initalization you have there is trying to default construct a temporary CTypedPtrMap
object to bind the reference to. However, only const
references can be bound to temporary objects. No doubt the error message is not very informative.
But even if the m_calcMap
member were a const
refernce, you'd still have a problem binding it to a temporary. in this case, the MSVC 2008 compiler gives a pretty clear warning:
mfctest.cpp(72) : warning C4413: '' : reference member is initialized to a temporary
that doesn't persist after the constructor exits
Uniform Initialization inside constructor/function declaration parameter list
It's a grammatical constraint. This specific grammar element is described in [dcl.fct] ¶3:
parameter-declaration-clause:
parameter-declaration-listopt ...opt
parameter-declaration-list , ...
parameter-declaration-list:
parameter-declaration
parameter-declaration-list , parameter-declaration
parameter-declaration:
attribute-specifier-seqopt decl-specifier-seq declarator
attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
attribute-specifier-seqopt decl-specifier-seq abstract-declaratoropt
attribute-specifier-seqopt decl-specifier-seq abstract-declaratoropt = initializer-clause
The thing to note is that the grammar permits an initializer-clause to be present only if it's preceded by a =
. I suspect it's to ensure there's no ambiguity between function declarations and object declarations. For instance, in block scope:
Widget foo(CompA{}, CompB{});
Has to be an object declaration, like the uniform initialization proposal wanted to make sure. Allowing plain {}
as a default argument would make the above ambiguous, and we'll just have another vexing parse to add to the collection. So a =
is required.
Now, as for why not allow it in a constructor where ambiguity is unlikely: the standard committee is not really in the habit of allowing a very constrained use-case if the more general one cannot be supported.
Uniform initializer used in default argument to const reference
It is valid in C++11, but it was a very late addition to the working paper that Bjarne put through. So it's not surprising that GCC doesn't support brace default arguments yet.
c++ initializer list issue - error: attempting to reference a deleted function
In the first variant, where you don't have a User
default constructor, the compiler will not create a default constructor for you. That means there is no way to default-construct (like you do in the ofApp
class) an object of the User
class.
There are two ways of solving the problem: The first one you already know and that is to create a default constructor. As a variant of this you could use the compilers default constructor by using
class User {
...
User() = default;
...
};
The other solution is to use default arguments for the other constructor, so it can be invoked without arguments like a default constructor:
class User {
...
User(ICoordinateMapper coordinateMapper = nullptr)
: _coordMapper(coordinateMapper){}
...
};
I would recommend the second way, as it will initialize the _coordMapper
member.
Related Topics
What Does an Ampersand After This Assignment Operator Mean
How to Use Dylib in MAC Os X (C++)
How to Force Gcc to Assume That a Floating-Point Expression Is Non-Negative
How to Enable_Shared_From_This of Both Parent and Derived
How to Merge Two Bst's Efficiently
C++ Best Way to Get Integer Division and Remainder
Add External Libraries to Cmakelist.Txt C++
Get Size of Terminal Window (Rows/Columns)
Image Edge Smoothing with Opencv
Is There a Compact Equivalent to Python Range() in C++/Stl
Std::Vector Reserve() and Push_Back() Is Faster Than Resize() and Array Index, Why
Interesting Behavior of Compiler with Namespaces
Memory Allocation Profiling in C++
When Do Programmers Use Empty Base Optimization (Ebo)
Linux Optimistic Malloc: Will New Always Throw When Out of Memory