In DR 2137 we have the following text (emphasis is mine):
It is not clear in code like the following that selecting a copy/move
constructor is the correct choice when an initializer list contains a
single element of the type being initialized, as required by issue
1467:
#include <initializer_list>
#include <iostream>
struct Q {
Q() { std::cout << "default\n"; }
Q(Q const&) { std::cout << "copy\n"; }
Q(Q&&) { std::cout << "move\n"; }
Q(std::initializer_list<Q>) { std::cout << "initializer list\n"; }
};
int main() {
Q x = Q { Q() };
}
Here the intent is that Q objects can contain other Q objects, but
this is broken by the resolution of issue 1467.
I'd like to understand why the code above would be broken by the resolution of issue 1467.
DR 2147 has this statement: "the intent is that Q objects can contain other Q objects". Given that, it assumes that if the user constructs a Q from a braced-init-list containing other Qs, that the intent of the user is to call the initializer_list constructor.
Given that assumption, it is therefore "broken" to not call the initializer_list constructor, which 1467 would cause.
Whether you agree with this logic or not, that's the thinking behind 2147. It's also the thinking that permits [over.match.list] to prioritize initializer_list constructors over any other constructor type. So in that way, it is consistent.
Related
In the following C++20 program I put by mistake one extra pair of curved braces {} in B{{{{A{}}}}}:
#include <iostream>
struct A
{
A() { std::cout << "A() "; }
A( A&& ) = delete;
~A() { std::cout << "~A() "; }
};
struct B { std::initializer_list<A> l; };
int main()
{
[[maybe_unused]] auto x = B{{{{A{}}}}};
std::cout << ". ";
}
Clang rejected it, however with a strange error:
error: call to deleted constructor of 'const A'
But to my surprise GCC accepted it( https://gcc.godbolt.org/z/aPWe13xfc ).
Could you please explain why GCC accepts it (how does it treat extra curved braces)?
B{…}, since the single element of the initializer list is not designated and is not of type B (as it has no type at all), is aggregate initialization ([dcl.init.list]/3.4). B::l is thus copy-initialized from {{{A{}}}}; it's a specialization of std::initializer_list, so /3.6 and /5 apply. An "array of 1 const A" is created, and {{A{}}} is the initializer for its single element.
We can thus reduce the code to
const A a = {{A{}}};
with no mention of B at all, and indeed Clang and GCC produce the same disagreement for this line. Clang appears to be correct to reject it: that initialization is sent to constructors by /3.7, and obviously there is no constructor that could be viable (thus the error about the deleted move constructor).
Oddly, removing the extra pair of braces here (or in the original) causes both compilers to accept:
const A a = {A{}};
despite the fact that A is not an aggregate and so /3.7 still applies. Presumably both compilers are overenthusastically performing "guaranteed copy elision" (albeit to different degrees), identifying the prvalue A{} with an object eventually to be initialized by it; that, however, takes place only per [dcl.init.general]/16.6.1, which never comes into play in this analysis.
I wondering which overload method resolution rule was applied here.
My purpose was create a new temporary instance using copy constructor,
and then pass that object to the method, so r-value reference passing.
And there are overloaded methods that accept l-value, r-value, so I expected r-value overloaded method will be invoked, but It was not.
class Kdy {
public:
Kdy() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Kdy(Kdy&&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Kdy(const Kdy&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void DoAction(const Kdy&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void DoAction(Kdy&&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
}; // Kdy
int main() {
Kdy kdy1;
Kdy kdy2;
// DoAction(const Kdy&), Why??
// kdy1.DoAction(Kdy(kdy2))
kdy1.DoAction({kdy2});
// Then why this works?
// After copy-ctor, DoAction(Kdy&&) was invoked.
kdy1.DoAction({ {kdy2} });
// Then why this dosen't compile?
// Since { {kdy2} } becomes Kdy&&
// { { {kdy2} } } should be Kdy(Kdy&&)
// kdy1.DoAction({ { {kdy2} } });
return 0;
}
I have read a overload reference document multiple times,
but it half clear to me
https://en.cppreference.com/w/cpp/language/overload_resolution
It seems after collecting a set of candidate methods,
compiler make a decision which method best matches by their matching priority.
So obviously if there are some methods accept std::initializer_list<Kdy> as parameter then those methods are selected. (I have tested it)
Still confusing then
if exact signature matching was failed,
which resolution overload rule was applied in this context?
What made compile think match {kdy2} best suit for const Kdy& than for Kdy&&?
Also, why { { { kdy2 } } } couldn't be interpreted as Kdy(Kdy&&)?
Please shed light on this poor guy.
Thank you!
Let's take the following part of the standard as reference:
[dcl.init.list]
(3.7) Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution.
(3.9) Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element ...
Section (3.9) explain why DoAction({kdy2}) select the overload DoAction(const Kdy&). The single element of the initializer list is an lvalue of type Kdy and from the two overloads of DoAction only one can bind to an lvalue; the one selected.
In DoAction({ {kdy2} }) the initializer don't have a single element of type Kdy, (3.9) is not used and a prvalue is introduced for {{kdy2}}. By (3.7) the constructors of Kdy are considered. Candidates are Kdy(Kdy&&) and Kdy(Kdy const&).
In order to select the best one, {kdy} is tried to be converted to the parameters of the ctors and again applying (3.9) the selected constructor is the copy ctor. The prvalue is then bound to the parameter of DoAction and from these overload DoAction(Kdy&&) is a better match.
For DoAction({ { {kdy2} } }) attemps are made as in the second case but when trying to convert {{kdy2}} to the parameters of the constructors it fails because the initializer list don't have a single element of type Kdy and (3.7) don't apply.
Is there any difference between the evaluation of expressions between the two return statements below, based on the extra parenthesis?
return a++ *(-b+123.456)/999.12344;
vs
return (a++ *(-b+123.456)/999.12344);
Programming Language C++
Standards CPP 98'ish (Before C++11)
Hope the question is clear. Expectation is to evaluate the expression in full.
Sloppy speaking x is the same as (x) (see this answer for the "striclty speaking" answer ;). Adding parentheses around the full expression does not change anything about operator precedence.
PS: It has an obscure impact on return value optimization (see this answer for the details). Though, it definitely has no impact on the value that is returned.
There are differences. Using parentheses will obviate return value optimisation.
If, for example, a and / or b were objects with all suitable operators overloaded, then using parentheses could suffer you the overhead of an object value copy.
For plain-old-data there is no difference, subject to the C++11 abomination
decltype(auto) ub_server() { int a; return (a);}
which actually gives you a dangling reference.
In summary: Don't use the enclosing parentheses unless you want some of the above behaviour, and possibly only then with a supporting comment.
Is there any difference between the evaluation of expressions between the two return statements below, based on the extra parenthesis?
No; the parentheses are completely redundant in this case.
An expression expr is actually not the same as an expression (expr), and you can observe this with return because copy/move elision is inhibited in the latter case:
#include <iostream>
struct T
{
T() { std::cout << "T()\n"; }
T(const T&) { std::cout << "T(const T&)\n"; }
T(T&&) { std::cout << "T(T&&)\n"; }
~T() { std::cout << "~T()\n"; }
};
T foo()
{
T t;
return t;
}
T bar()
{
T t;
return (t);
}
int main()
{
std::cout << "Test 1\n------\n";
foo();
std::cout << '\n';
std::cout << "Test 2\n------\n";
bar();
}
Output:
Test 1
------
T()
~T()
Test 2
------
T()
T(T&&)
~T()
~T()
(live demo)
You can probably observe the same result before C++17 because compilers have always tried to optimise return values. Even in your standard, C++98, you can probably observe that the copy constructor isn't invoked in the first case.
But, hey, none of that is relevant for a simple arithmetic expression.
DIRECT- VS COPY-INITIALIZATION
Through this question (Is it direct-initialization or copy-initialization?) I learned the differences between direct-initialization and copy-initialization:
direct-initialization copy-initialization
----------------------- ---------------------
obj s("value"); obj s = obj("value");
obj s = "value";
obj s{"value"}; obj s = {"value"};
obj s = obj{"value"};
I mention it here for the sake of completeness. My actual questions for this page are listed in the next paragraph >>
DIRECT-INITIALIZATION VS DIRECT-LIST-INITIALIZATION
The answers revealed that within the category of direct-initialization, one can make a difference between direct-initialization and direct-list-initialization.:
obj s("value"); // direct-initialization
obj s{"value"}; // direct-list-initialization
I know that list-initialization doesn't allow narrowing, such that an initialization like int x{3.5}; won't compile. But besides this, I got a couple of questions:
(1) Is there any difference in compiler output between
obj s("value"); and obj s{"value"};?
Let's consider a compiler without any optimizations. I would like to know any possible technical difference :-)
(2) Perhaps I should ask the exact same question for a multi-variable initialization, like:
obj s("val1", "val2"); and obj s{"val1", "val2"};
(3) I have noticed that the list-initialization can sometimes call a different constructor, like in:
vector<int> a{10,20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10,20); //Parentesis -> uses arguments to parameterize some functionality
How is that possible?
DID WE COVER ALL POSSIBLE INITIALIZATIONS HERE?
From my limited knowledge on C++, I believe that all possible initializations of objects (either native-typed or user-defined-typed objects) have been covered in the examples above. Is that correct? Did I overlook something?
PS: I am learning C++ (I do know C, but not yet C++), so please don't be too hard on me ;-)
(1) Is there any difference in compiler output between
obj s("value"); and obj s{"value"};? Let's consider a compiler
without any optimizations. I would like to know any possible technical
difference :-)
(2) Perhaps I should ask the exact same question for a
multi-variable initialization, like: obj s("val1", "val2"); and
obj s{"val1", "val2"};
(3) I have noticed that the list-initialization can
sometimes call a different constructor, like in:
vector<int> a{10,20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10,20); //Parentesis -> uses arguments to parameterize some functionality
How is that possible?
If there is an initializer-list constructor for the class type obj, it will always be preferred over other other constructors for brace-init-initializers (list initialization), in your case obj s{"value"};;
What this means is that if you have a constructor that takes std::initializer_list<T> as its first parameter and other parameters are defaulted, then it is preferred. Example
struct A{
A(std::initializer_list<std::string>); //Always be preferred for A a{"value"}
A(std::string);
};
std::vector<T> and other STL containers have such initializer-list constructors.
Otherwise, Overload resolution kicks in and it falls back to any available constructor as selected by the overload resolution process;
Otherwise, if the class has no user defined constructors and it's an aggregate type, it initializes the class members directly.
DID WE COVER ALL POSSIBLE INITIALIZATIONS HERE? From my
limited knowledge on C++, I believe that all possible initializations
of objects (either native-typed or user-defined-typed objects) have
been covered in the examples above. Is that correct? Did I overlook
something?
Nope. you didn't. Excluding Reference initialization, there are five ways objects may be initialized in C++.
Direct Initialization
List Initialization
Copy Initialization
Value Initialization
Aggregate Initialization (only for aggregate types)
You can find more information here
List-initialization guarantee left-to-right order of arguments evaluation. In this example we will create std::tuple from istream data and then output tuple example can be found here:
#include <iostream>
#include <sstream>
#include <tuple>
template<typename T, typename CharT>
T extract(std::basic_istream<CharT>& is) {
T val;
is >> val;
return val;
}
void print(const std::tuple<int, long, double>& t) {
using std::cout;
cout << std::get<0>(t) << " " << std::get<1>(t) << " " << std::get<2>(t) << std::endl;
}
int main()
{
std::stringstream ss1;
std::stringstream ss2;
ss1 << 1 << " " << 2 << " " << 3;
ss2 << 1 << " " << 2 << " " << 3;
auto compilerOrder = std::tuple<int, long, double>( extract<int>(ss1), extract<long>(ss1), extract<double>(ss1) );
auto leftToRightOrder = std::tuple<int, long, double>{ extract<int>(ss2), extract<long>(ss2), extract<double>(ss2) };
print(compilerOrder);
print(leftToRightOrder);
}
Output:
3 2 1
1 2 3
As you can see, the difference will be seen then we use multiple times same stream-like resource inside function brackets.
Also my question about that
I am writing a generic function like below.
template<class Iterator, class T>
void foo(Iterator first, Iterator last) {
T a;
cout << a << endl;
// do something with iterators
}
typedef vector<double>::iterator DblPtr;
vector<double> values;
foo< DblPtr, int>();
This functions prints out an undefined value for variable a, while if I change the initialization into
///
T a = T()
cout << a << endl;
// do something with iterators
I can see that the initialized value is 0 as I am expecting.
If I call T a the variable is initialized with the default value, but if i call T a = T() I believe that due to optimization the copy constructor should be called with the value of T() that is still the default one.
I cannot understand what is the difference behind these 2 lines and the reason why this happens?
First of all, default initiaization of built-in types such as int leaves them uninitialized. Value initialization leaves them zero-initialized. As for your example
This is a default initialization:
T a;
This is a value initialization, using copy initialization:
T a = T();
You are right that copies can be elided here, so this has the effect of creating a single value-initialized T object. However, it does require that T be copyable or move-copyable. This is the case with built-in types, but it is a restriction to bear in mind.
The copy initialization syntax is required because this is a function declaration:
T a();
but C++11 allows you to value-initialize like this:
T a{};