Perfect forwarding in constructors (C++17)
up vote
13
down vote
favorite
Consider the following code
struct A {
A(int id) : id_ { id } {}
A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }
int id_;
};
template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}
T x_;
};
template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;
T x_;
};
template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}
int
main(
) {
A a { 1 };
//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };
B2 b21 { a };
B2 b22 { A { 3 } };
return 0;
}
which yields
mctor from 2
mctor from 3
So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.
Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?
Links to the appropriate section of the standard would be welcome.
I am using GCC 7.2.0.
c++ gcc language-lawyer c++17 template-deduction
|
show 2 more comments
up vote
13
down vote
favorite
Consider the following code
struct A {
A(int id) : id_ { id } {}
A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }
int id_;
};
template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}
T x_;
};
template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;
T x_;
};
template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}
int
main(
) {
A a { 1 };
//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };
B2 b21 { a };
B2 b22 { A { 3 } };
return 0;
}
which yields
mctor from 2
mctor from 3
So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.
Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?
Links to the appropriate section of the standard would be welcome.
I am using GCC 7.2.0.
c++ gcc language-lawyer c++17 template-deduction
2
Clang won't compile thisB2 b21 { a };
– AndyG
Nov 28 at 12:46
4
I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51
1
@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction withT&&
(withT
from class).
– Jarod42
Nov 28 at 12:56
1
To clarify OP's question: shouldb21
beB2<A&>
, or should it not compile ?
– Jarod42
Nov 28 at 12:58
1
B1
andB2
should behave the same.
– Jarod42
Nov 28 at 13:00
|
show 2 more comments
up vote
13
down vote
favorite
up vote
13
down vote
favorite
Consider the following code
struct A {
A(int id) : id_ { id } {}
A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }
int id_;
};
template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}
T x_;
};
template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;
T x_;
};
template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}
int
main(
) {
A a { 1 };
//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };
B2 b21 { a };
B2 b22 { A { 3 } };
return 0;
}
which yields
mctor from 2
mctor from 3
So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.
Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?
Links to the appropriate section of the standard would be welcome.
I am using GCC 7.2.0.
c++ gcc language-lawyer c++17 template-deduction
Consider the following code
struct A {
A(int id) : id_ { id } {}
A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }
int id_;
};
template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}
T x_;
};
template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;
T x_;
};
template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}
int
main(
) {
A a { 1 };
//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };
B2 b21 { a };
B2 b22 { A { 3 } };
return 0;
}
which yields
mctor from 2
mctor from 3
So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.
Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?
Links to the appropriate section of the standard would be welcome.
I am using GCC 7.2.0.
c++ gcc language-lawyer c++17 template-deduction
c++ gcc language-lawyer c++17 template-deduction
edited Nov 28 at 17:59
Peter Mortensen
13.4k1983111
13.4k1983111
asked Nov 28 at 12:39
plexando
9011
9011
2
Clang won't compile thisB2 b21 { a };
– AndyG
Nov 28 at 12:46
4
I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51
1
@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction withT&&
(withT
from class).
– Jarod42
Nov 28 at 12:56
1
To clarify OP's question: shouldb21
beB2<A&>
, or should it not compile ?
– Jarod42
Nov 28 at 12:58
1
B1
andB2
should behave the same.
– Jarod42
Nov 28 at 13:00
|
show 2 more comments
2
Clang won't compile thisB2 b21 { a };
– AndyG
Nov 28 at 12:46
4
I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51
1
@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction withT&&
(withT
from class).
– Jarod42
Nov 28 at 12:56
1
To clarify OP's question: shouldb21
beB2<A&>
, or should it not compile ?
– Jarod42
Nov 28 at 12:58
1
B1
andB2
should behave the same.
– Jarod42
Nov 28 at 13:00
2
2
Clang won't compile this
B2 b21 { a };
– AndyG
Nov 28 at 12:46
Clang won't compile this
B2 b21 { a };
– AndyG
Nov 28 at 12:46
4
4
I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51
I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51
1
1
@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with
T&&
(with T
from class).– Jarod42
Nov 28 at 12:56
@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with
T&&
(with T
from class).– Jarod42
Nov 28 at 12:56
1
1
To clarify OP's question: should
b21
be B2<A&>
, or should it not compile ?– Jarod42
Nov 28 at 12:58
To clarify OP's question: should
b21
be B2<A&>
, or should it not compile ?– Jarod42
Nov 28 at 12:58
1
1
B1
and B2
should behave the same.– Jarod42
Nov 28 at 13:00
B1
and B2
should behave the same.– Jarod42
Nov 28 at 13:00
|
show 2 more comments
2 Answers
2
active
oldest
votes
up vote
9
down vote
accepted
It's a GCC bug. Forwarding references have a very clear cut definition:
[temp.deduct.call] (emphasis mine)
3 A forwarding reference is an rvalue reference to a
cv-unqualified template parameter that does not represent a template
parameter of a class template (during class template argument
deduction ([over.match.class.deduct])). If P is a forwarding reference
and the argument is an lvalue, the type “lvalue reference to A” is
used in place of A for type deduction.
In both cases T
names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.
You wouldn't happen to have any idea if this has been reported to the project?
– jaked122
Nov 28 at 18:11
1
@jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
– StoryTeller
Nov 28 at 18:14
Bug report done #88252
– Oliv
Nov 28 at 20:01
Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
– plexando
Nov 29 at 11:21
@plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
– StoryTeller
Nov 29 at 11:25
add a comment |
up vote
6
down vote
It looks that GCC incorrectly treats T&&
in an auto-generated deduction guide as a forwarding reference:
template <typename T>
B2(T&& x) -> B2<T>;
In this case T&&
is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A&
parameter type and B2<T>=B2<A&>
class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:
constexpr B2(A& x) noexcept;
Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a };
should fail.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53519699%2fperfect-forwarding-in-constructors-c17%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
9
down vote
accepted
It's a GCC bug. Forwarding references have a very clear cut definition:
[temp.deduct.call] (emphasis mine)
3 A forwarding reference is an rvalue reference to a
cv-unqualified template parameter that does not represent a template
parameter of a class template (during class template argument
deduction ([over.match.class.deduct])). If P is a forwarding reference
and the argument is an lvalue, the type “lvalue reference to A” is
used in place of A for type deduction.
In both cases T
names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.
You wouldn't happen to have any idea if this has been reported to the project?
– jaked122
Nov 28 at 18:11
1
@jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
– StoryTeller
Nov 28 at 18:14
Bug report done #88252
– Oliv
Nov 28 at 20:01
Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
– plexando
Nov 29 at 11:21
@plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
– StoryTeller
Nov 29 at 11:25
add a comment |
up vote
9
down vote
accepted
It's a GCC bug. Forwarding references have a very clear cut definition:
[temp.deduct.call] (emphasis mine)
3 A forwarding reference is an rvalue reference to a
cv-unqualified template parameter that does not represent a template
parameter of a class template (during class template argument
deduction ([over.match.class.deduct])). If P is a forwarding reference
and the argument is an lvalue, the type “lvalue reference to A” is
used in place of A for type deduction.
In both cases T
names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.
You wouldn't happen to have any idea if this has been reported to the project?
– jaked122
Nov 28 at 18:11
1
@jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
– StoryTeller
Nov 28 at 18:14
Bug report done #88252
– Oliv
Nov 28 at 20:01
Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
– plexando
Nov 29 at 11:21
@plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
– StoryTeller
Nov 29 at 11:25
add a comment |
up vote
9
down vote
accepted
up vote
9
down vote
accepted
It's a GCC bug. Forwarding references have a very clear cut definition:
[temp.deduct.call] (emphasis mine)
3 A forwarding reference is an rvalue reference to a
cv-unqualified template parameter that does not represent a template
parameter of a class template (during class template argument
deduction ([over.match.class.deduct])). If P is a forwarding reference
and the argument is an lvalue, the type “lvalue reference to A” is
used in place of A for type deduction.
In both cases T
names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.
It's a GCC bug. Forwarding references have a very clear cut definition:
[temp.deduct.call] (emphasis mine)
3 A forwarding reference is an rvalue reference to a
cv-unqualified template parameter that does not represent a template
parameter of a class template (during class template argument
deduction ([over.match.class.deduct])). If P is a forwarding reference
and the argument is an lvalue, the type “lvalue reference to A” is
used in place of A for type deduction.
In both cases T
names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.
answered Nov 28 at 12:53
StoryTeller
92.3k12183249
92.3k12183249
You wouldn't happen to have any idea if this has been reported to the project?
– jaked122
Nov 28 at 18:11
1
@jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
– StoryTeller
Nov 28 at 18:14
Bug report done #88252
– Oliv
Nov 28 at 20:01
Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
– plexando
Nov 29 at 11:21
@plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
– StoryTeller
Nov 29 at 11:25
add a comment |
You wouldn't happen to have any idea if this has been reported to the project?
– jaked122
Nov 28 at 18:11
1
@jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
– StoryTeller
Nov 28 at 18:14
Bug report done #88252
– Oliv
Nov 28 at 20:01
Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
– plexando
Nov 29 at 11:21
@plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
– StoryTeller
Nov 29 at 11:25
You wouldn't happen to have any idea if this has been reported to the project?
– jaked122
Nov 28 at 18:11
You wouldn't happen to have any idea if this has been reported to the project?
– jaked122
Nov 28 at 18:11
1
1
@jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
– StoryTeller
Nov 28 at 18:14
@jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
– StoryTeller
Nov 28 at 18:14
Bug report done #88252
– Oliv
Nov 28 at 20:01
Bug report done #88252
– Oliv
Nov 28 at 20:01
Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
– plexando
Nov 29 at 11:21
Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
– plexando
Nov 29 at 11:21
@plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
– StoryTeller
Nov 29 at 11:25
@plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
– StoryTeller
Nov 29 at 11:25
add a comment |
up vote
6
down vote
It looks that GCC incorrectly treats T&&
in an auto-generated deduction guide as a forwarding reference:
template <typename T>
B2(T&& x) -> B2<T>;
In this case T&&
is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A&
parameter type and B2<T>=B2<A&>
class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:
constexpr B2(A& x) noexcept;
Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a };
should fail.
add a comment |
up vote
6
down vote
It looks that GCC incorrectly treats T&&
in an auto-generated deduction guide as a forwarding reference:
template <typename T>
B2(T&& x) -> B2<T>;
In this case T&&
is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A&
parameter type and B2<T>=B2<A&>
class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:
constexpr B2(A& x) noexcept;
Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a };
should fail.
add a comment |
up vote
6
down vote
up vote
6
down vote
It looks that GCC incorrectly treats T&&
in an auto-generated deduction guide as a forwarding reference:
template <typename T>
B2(T&& x) -> B2<T>;
In this case T&&
is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A&
parameter type and B2<T>=B2<A&>
class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:
constexpr B2(A& x) noexcept;
Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a };
should fail.
It looks that GCC incorrectly treats T&&
in an auto-generated deduction guide as a forwarding reference:
template <typename T>
B2(T&& x) -> B2<T>;
In this case T&&
is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A&
parameter type and B2<T>=B2<A&>
class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:
constexpr B2(A& x) noexcept;
Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a };
should fail.
answered Nov 28 at 13:11
Piotr Skotnicki
34.4k470117
34.4k470117
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53519699%2fperfect-forwarding-in-constructors-c17%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
Clang won't compile this
B2 b21 { a };
– AndyG
Nov 28 at 12:46
4
I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51
1
@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with
T&&
(withT
from class).– Jarod42
Nov 28 at 12:56
1
To clarify OP's question: should
b21
beB2<A&>
, or should it not compile ?– Jarod42
Nov 28 at 12:58
1
B1
andB2
should behave the same.– Jarod42
Nov 28 at 13:00