Perfect forwarding of a callable
I came up with the following code to transform a R()
-like into a void()
-like callable:
#include <utility>
template<class Callable>
auto discardable(Callable&& callable)
{ return [&]() { (void) std::forward<Callable>(callable)(); }; }
// ^-- is it ok?
int main()
{
auto f = discardable([n=42]() mutable { return n--; });
f();
}
I'm worried about the capture by reference.
- Is it well-defined?
- Am I guaranteed that
callable
is never copied and never used after its lifetime has ended?
This is tagged C++14, but applies to all following standards.
c++ lambda c++14 perfect-forwarding
add a comment |
I came up with the following code to transform a R()
-like into a void()
-like callable:
#include <utility>
template<class Callable>
auto discardable(Callable&& callable)
{ return [&]() { (void) std::forward<Callable>(callable)(); }; }
// ^-- is it ok?
int main()
{
auto f = discardable([n=42]() mutable { return n--; });
f();
}
I'm worried about the capture by reference.
- Is it well-defined?
- Am I guaranteed that
callable
is never copied and never used after its lifetime has ended?
This is tagged C++14, but applies to all following standards.
c++ lambda c++14 perfect-forwarding
4
Sincecallable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture.
– Maxim Egorushkin
Jan 29 at 10:47
add a comment |
I came up with the following code to transform a R()
-like into a void()
-like callable:
#include <utility>
template<class Callable>
auto discardable(Callable&& callable)
{ return [&]() { (void) std::forward<Callable>(callable)(); }; }
// ^-- is it ok?
int main()
{
auto f = discardable([n=42]() mutable { return n--; });
f();
}
I'm worried about the capture by reference.
- Is it well-defined?
- Am I guaranteed that
callable
is never copied and never used after its lifetime has ended?
This is tagged C++14, but applies to all following standards.
c++ lambda c++14 perfect-forwarding
I came up with the following code to transform a R()
-like into a void()
-like callable:
#include <utility>
template<class Callable>
auto discardable(Callable&& callable)
{ return [&]() { (void) std::forward<Callable>(callable)(); }; }
// ^-- is it ok?
int main()
{
auto f = discardable([n=42]() mutable { return n--; });
f();
}
I'm worried about the capture by reference.
- Is it well-defined?
- Am I guaranteed that
callable
is never copied and never used after its lifetime has ended?
This is tagged C++14, but applies to all following standards.
c++ lambda c++14 perfect-forwarding
c++ lambda c++14 perfect-forwarding
edited Feb 1 at 17:39
Yvette Colomb♦
20.3k1470110
20.3k1470110
asked Jan 29 at 10:28
YSCYSC
24k554109
24k554109
4
Sincecallable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture.
– Maxim Egorushkin
Jan 29 at 10:47
add a comment |
4
Sincecallable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture.
– Maxim Egorushkin
Jan 29 at 10:47
4
4
Since
callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture.– Maxim Egorushkin
Jan 29 at 10:47
Since
callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture.– Maxim Egorushkin
Jan 29 at 10:47
add a comment |
3 Answers
3
active
oldest
votes
Lambdas are anonymous structs with an operator()
, the capture list is a fancy way of specifying the type of its members. Capturing by reference really is just what it sounds like: you have reference members. It isn't hard to see the reference dangles.
This is a case where you specifically don't want to perfectly forward: you have different semantics depending on whether the argument is a lvalue or rvalue reference.
template<class Callable>
auto discardable(Callable& callable)
{
return [&]() mutable { (void) callable(); };
}
template<class Callable>
auto discardable(Callable&& callable)
{
return [callable = std::forward<Callable>(callable)]() mutable { // move, don't copy
(void) std::move(callable)(); // If you want rvalue semantics
};
}
1
Are you sureforward
andmove
should not be reversed?
– Maxim Egorushkin
Jan 29 at 12:40
@MaximEgorushkin nop. It is correct. But I'd rather use move on both; it is less error-prone. Forward should almost always get called with its type parameter explicitly provided, which is a source of errors due to chance to omit that parameter.
– Red.Wave
Jan 29 at 13:46
@MaximEgorushkin Theforward
andmove
is like that because I find it easier to understand in those contexts. The first says "whatever type it was, use the same", the second says "cast this lvalue to xvalue". In the end, it's preference I guess. Fixed compile errors BTW.
– Passer By
Jan 29 at 14:32
(void) std::move(callable)();
- I don't know OP's intent is, but this basically means that in caseoperator()
is a one-time operation onCallable&&
, the lambda can only be called safely once, which does not feel right.
– Holt
Jan 29 at 20:39
@Holt It may or may not be intentional by OP, I can't tell. The use ofstd::move
and the comment should hopefully attract enough attention to the matter.
– Passer By
Jan 31 at 15:36
add a comment |
Since callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture. To prevent that, if an argument is an r-value it needs to be copied.
A working example:
template<class Callable>
auto discardable(Callable&& callable) { // This one makes a copy of the temporary.
return [callable = std::move(callable)]() mutable {
static_cast<void>(static_cast<Callable&&>(callable)());
};
}
template<class Callable>
auto discardable(Callable& callable) {
return [&callable]() mutable {
static_cast<void>(callable());
};
}
You can still face lifetime issues if callable
is an l-value reference but its lifetime scope is smaller than that of the lambda capture returned by discardable
. So, it may be the safest and easiest to always move or copy callable
.
As a side note, although there are new specialised utilities that perfect-forward the value category of the function object, like std::apply
, the standard library algorithms always copy function objects by accepting them by value. So that if one overloaded both operator()()&
and operator()()&&
the standard library would always use operator()()&
.
Does it needstd::move
in the capture thingie? So as to avoid a copy if unnecessary? Genuine question; I am a little behind on the latest gadgets.
– Lightness Races in Orbit
Jan 29 at 11:01
2
@LightnessRacesinOrbit Someone may declareoperator()()&&
along withoperator()()&
. IMO, that is brittle, but conceivable. Although the standard library just makes copies of function objects and is not concerned with such trifles.
– Maxim Egorushkin
Jan 29 at 11:03
1
Just seems like a wasted copy for nothing. You can still invokeoperator()()&&
on it
– Lightness Races in Orbit
Jan 29 at 11:04
2
"the standard library always copies function objects by accepting them by value". That's what STL did when forwarding references didn't exist. std::apply uses forwarding reference as counter-example.
– Jarod42
Jan 29 at 11:10
1
So, why would you copy when you can move? I totally don't get it.return [callable = std::move(callable)]() mutable {…}
– Arne Vogel
Jan 29 at 12:22
|
show 11 more comments
Your program is UB as you use dangling reference of the captured lambda.
So to perfect forward capture in lambda, you may use
template<class Callable>
auto discardable(Callable&& callable)
{
return [f = std::conditional_t<
std::is_lvalue_reference<Callable>::value,
std::reference_wrapper<std::remove_reference_t<Callable>>,
Callable>{std::forward<Callable>(callable)}]
{
std::forward<Callable>(f)();
};
}
It move-constructs the temporary lambda.
1
Is forwarding captured variable really necessary?
– bartop
Jan 29 at 10:54
IMO you should not forwardf
since you may want to call the lambda multiple times.
– Holt
Jan 29 at 20:38
@Holt: Thatforward
/move
only changes category value of the functor. There is no assignment to transfer ownership.operator()()&&
might invalidate itself, but in that case, presence of this kind of overload might mean that user wants to respect category value.
– Jarod42
Jan 29 at 20:48
@Jarod42 I am talking aboutoperator()()&&
invalidatingf
as you mention. I don't know OP's intent, I'm just saying this might be worth mentioning.
– Holt
Jan 29 at 20:50
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',
autoActivateHeartbeat: false,
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%2f54418941%2fperfect-forwarding-of-a-callable%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Lambdas are anonymous structs with an operator()
, the capture list is a fancy way of specifying the type of its members. Capturing by reference really is just what it sounds like: you have reference members. It isn't hard to see the reference dangles.
This is a case where you specifically don't want to perfectly forward: you have different semantics depending on whether the argument is a lvalue or rvalue reference.
template<class Callable>
auto discardable(Callable& callable)
{
return [&]() mutable { (void) callable(); };
}
template<class Callable>
auto discardable(Callable&& callable)
{
return [callable = std::forward<Callable>(callable)]() mutable { // move, don't copy
(void) std::move(callable)(); // If you want rvalue semantics
};
}
1
Are you sureforward
andmove
should not be reversed?
– Maxim Egorushkin
Jan 29 at 12:40
@MaximEgorushkin nop. It is correct. But I'd rather use move on both; it is less error-prone. Forward should almost always get called with its type parameter explicitly provided, which is a source of errors due to chance to omit that parameter.
– Red.Wave
Jan 29 at 13:46
@MaximEgorushkin Theforward
andmove
is like that because I find it easier to understand in those contexts. The first says "whatever type it was, use the same", the second says "cast this lvalue to xvalue". In the end, it's preference I guess. Fixed compile errors BTW.
– Passer By
Jan 29 at 14:32
(void) std::move(callable)();
- I don't know OP's intent is, but this basically means that in caseoperator()
is a one-time operation onCallable&&
, the lambda can only be called safely once, which does not feel right.
– Holt
Jan 29 at 20:39
@Holt It may or may not be intentional by OP, I can't tell. The use ofstd::move
and the comment should hopefully attract enough attention to the matter.
– Passer By
Jan 31 at 15:36
add a comment |
Lambdas are anonymous structs with an operator()
, the capture list is a fancy way of specifying the type of its members. Capturing by reference really is just what it sounds like: you have reference members. It isn't hard to see the reference dangles.
This is a case where you specifically don't want to perfectly forward: you have different semantics depending on whether the argument is a lvalue or rvalue reference.
template<class Callable>
auto discardable(Callable& callable)
{
return [&]() mutable { (void) callable(); };
}
template<class Callable>
auto discardable(Callable&& callable)
{
return [callable = std::forward<Callable>(callable)]() mutable { // move, don't copy
(void) std::move(callable)(); // If you want rvalue semantics
};
}
1
Are you sureforward
andmove
should not be reversed?
– Maxim Egorushkin
Jan 29 at 12:40
@MaximEgorushkin nop. It is correct. But I'd rather use move on both; it is less error-prone. Forward should almost always get called with its type parameter explicitly provided, which is a source of errors due to chance to omit that parameter.
– Red.Wave
Jan 29 at 13:46
@MaximEgorushkin Theforward
andmove
is like that because I find it easier to understand in those contexts. The first says "whatever type it was, use the same", the second says "cast this lvalue to xvalue". In the end, it's preference I guess. Fixed compile errors BTW.
– Passer By
Jan 29 at 14:32
(void) std::move(callable)();
- I don't know OP's intent is, but this basically means that in caseoperator()
is a one-time operation onCallable&&
, the lambda can only be called safely once, which does not feel right.
– Holt
Jan 29 at 20:39
@Holt It may or may not be intentional by OP, I can't tell. The use ofstd::move
and the comment should hopefully attract enough attention to the matter.
– Passer By
Jan 31 at 15:36
add a comment |
Lambdas are anonymous structs with an operator()
, the capture list is a fancy way of specifying the type of its members. Capturing by reference really is just what it sounds like: you have reference members. It isn't hard to see the reference dangles.
This is a case where you specifically don't want to perfectly forward: you have different semantics depending on whether the argument is a lvalue or rvalue reference.
template<class Callable>
auto discardable(Callable& callable)
{
return [&]() mutable { (void) callable(); };
}
template<class Callable>
auto discardable(Callable&& callable)
{
return [callable = std::forward<Callable>(callable)]() mutable { // move, don't copy
(void) std::move(callable)(); // If you want rvalue semantics
};
}
Lambdas are anonymous structs with an operator()
, the capture list is a fancy way of specifying the type of its members. Capturing by reference really is just what it sounds like: you have reference members. It isn't hard to see the reference dangles.
This is a case where you specifically don't want to perfectly forward: you have different semantics depending on whether the argument is a lvalue or rvalue reference.
template<class Callable>
auto discardable(Callable& callable)
{
return [&]() mutable { (void) callable(); };
}
template<class Callable>
auto discardable(Callable&& callable)
{
return [callable = std::forward<Callable>(callable)]() mutable { // move, don't copy
(void) std::move(callable)(); // If you want rvalue semantics
};
}
edited Jan 29 at 14:29
answered Jan 29 at 11:42
Passer ByPasser By
9,84032557
9,84032557
1
Are you sureforward
andmove
should not be reversed?
– Maxim Egorushkin
Jan 29 at 12:40
@MaximEgorushkin nop. It is correct. But I'd rather use move on both; it is less error-prone. Forward should almost always get called with its type parameter explicitly provided, which is a source of errors due to chance to omit that parameter.
– Red.Wave
Jan 29 at 13:46
@MaximEgorushkin Theforward
andmove
is like that because I find it easier to understand in those contexts. The first says "whatever type it was, use the same", the second says "cast this lvalue to xvalue". In the end, it's preference I guess. Fixed compile errors BTW.
– Passer By
Jan 29 at 14:32
(void) std::move(callable)();
- I don't know OP's intent is, but this basically means that in caseoperator()
is a one-time operation onCallable&&
, the lambda can only be called safely once, which does not feel right.
– Holt
Jan 29 at 20:39
@Holt It may or may not be intentional by OP, I can't tell. The use ofstd::move
and the comment should hopefully attract enough attention to the matter.
– Passer By
Jan 31 at 15:36
add a comment |
1
Are you sureforward
andmove
should not be reversed?
– Maxim Egorushkin
Jan 29 at 12:40
@MaximEgorushkin nop. It is correct. But I'd rather use move on both; it is less error-prone. Forward should almost always get called with its type parameter explicitly provided, which is a source of errors due to chance to omit that parameter.
– Red.Wave
Jan 29 at 13:46
@MaximEgorushkin Theforward
andmove
is like that because I find it easier to understand in those contexts. The first says "whatever type it was, use the same", the second says "cast this lvalue to xvalue". In the end, it's preference I guess. Fixed compile errors BTW.
– Passer By
Jan 29 at 14:32
(void) std::move(callable)();
- I don't know OP's intent is, but this basically means that in caseoperator()
is a one-time operation onCallable&&
, the lambda can only be called safely once, which does not feel right.
– Holt
Jan 29 at 20:39
@Holt It may or may not be intentional by OP, I can't tell. The use ofstd::move
and the comment should hopefully attract enough attention to the matter.
– Passer By
Jan 31 at 15:36
1
1
Are you sure
forward
and move
should not be reversed?– Maxim Egorushkin
Jan 29 at 12:40
Are you sure
forward
and move
should not be reversed?– Maxim Egorushkin
Jan 29 at 12:40
@MaximEgorushkin nop. It is correct. But I'd rather use move on both; it is less error-prone. Forward should almost always get called with its type parameter explicitly provided, which is a source of errors due to chance to omit that parameter.
– Red.Wave
Jan 29 at 13:46
@MaximEgorushkin nop. It is correct. But I'd rather use move on both; it is less error-prone. Forward should almost always get called with its type parameter explicitly provided, which is a source of errors due to chance to omit that parameter.
– Red.Wave
Jan 29 at 13:46
@MaximEgorushkin The
forward
and move
is like that because I find it easier to understand in those contexts. The first says "whatever type it was, use the same", the second says "cast this lvalue to xvalue". In the end, it's preference I guess. Fixed compile errors BTW.– Passer By
Jan 29 at 14:32
@MaximEgorushkin The
forward
and move
is like that because I find it easier to understand in those contexts. The first says "whatever type it was, use the same", the second says "cast this lvalue to xvalue". In the end, it's preference I guess. Fixed compile errors BTW.– Passer By
Jan 29 at 14:32
(void) std::move(callable)();
- I don't know OP's intent is, but this basically means that in case operator()
is a one-time operation on Callable&&
, the lambda can only be called safely once, which does not feel right.– Holt
Jan 29 at 20:39
(void) std::move(callable)();
- I don't know OP's intent is, but this basically means that in case operator()
is a one-time operation on Callable&&
, the lambda can only be called safely once, which does not feel right.– Holt
Jan 29 at 20:39
@Holt It may or may not be intentional by OP, I can't tell. The use of
std::move
and the comment should hopefully attract enough attention to the matter.– Passer By
Jan 31 at 15:36
@Holt It may or may not be intentional by OP, I can't tell. The use of
std::move
and the comment should hopefully attract enough attention to the matter.– Passer By
Jan 31 at 15:36
add a comment |
Since callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture. To prevent that, if an argument is an r-value it needs to be copied.
A working example:
template<class Callable>
auto discardable(Callable&& callable) { // This one makes a copy of the temporary.
return [callable = std::move(callable)]() mutable {
static_cast<void>(static_cast<Callable&&>(callable)());
};
}
template<class Callable>
auto discardable(Callable& callable) {
return [&callable]() mutable {
static_cast<void>(callable());
};
}
You can still face lifetime issues if callable
is an l-value reference but its lifetime scope is smaller than that of the lambda capture returned by discardable
. So, it may be the safest and easiest to always move or copy callable
.
As a side note, although there are new specialised utilities that perfect-forward the value category of the function object, like std::apply
, the standard library algorithms always copy function objects by accepting them by value. So that if one overloaded both operator()()&
and operator()()&&
the standard library would always use operator()()&
.
Does it needstd::move
in the capture thingie? So as to avoid a copy if unnecessary? Genuine question; I am a little behind on the latest gadgets.
– Lightness Races in Orbit
Jan 29 at 11:01
2
@LightnessRacesinOrbit Someone may declareoperator()()&&
along withoperator()()&
. IMO, that is brittle, but conceivable. Although the standard library just makes copies of function objects and is not concerned with such trifles.
– Maxim Egorushkin
Jan 29 at 11:03
1
Just seems like a wasted copy for nothing. You can still invokeoperator()()&&
on it
– Lightness Races in Orbit
Jan 29 at 11:04
2
"the standard library always copies function objects by accepting them by value". That's what STL did when forwarding references didn't exist. std::apply uses forwarding reference as counter-example.
– Jarod42
Jan 29 at 11:10
1
So, why would you copy when you can move? I totally don't get it.return [callable = std::move(callable)]() mutable {…}
– Arne Vogel
Jan 29 at 12:22
|
show 11 more comments
Since callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture. To prevent that, if an argument is an r-value it needs to be copied.
A working example:
template<class Callable>
auto discardable(Callable&& callable) { // This one makes a copy of the temporary.
return [callable = std::move(callable)]() mutable {
static_cast<void>(static_cast<Callable&&>(callable)());
};
}
template<class Callable>
auto discardable(Callable& callable) {
return [&callable]() mutable {
static_cast<void>(callable());
};
}
You can still face lifetime issues if callable
is an l-value reference but its lifetime scope is smaller than that of the lambda capture returned by discardable
. So, it may be the safest and easiest to always move or copy callable
.
As a side note, although there are new specialised utilities that perfect-forward the value category of the function object, like std::apply
, the standard library algorithms always copy function objects by accepting them by value. So that if one overloaded both operator()()&
and operator()()&&
the standard library would always use operator()()&
.
Does it needstd::move
in the capture thingie? So as to avoid a copy if unnecessary? Genuine question; I am a little behind on the latest gadgets.
– Lightness Races in Orbit
Jan 29 at 11:01
2
@LightnessRacesinOrbit Someone may declareoperator()()&&
along withoperator()()&
. IMO, that is brittle, but conceivable. Although the standard library just makes copies of function objects and is not concerned with such trifles.
– Maxim Egorushkin
Jan 29 at 11:03
1
Just seems like a wasted copy for nothing. You can still invokeoperator()()&&
on it
– Lightness Races in Orbit
Jan 29 at 11:04
2
"the standard library always copies function objects by accepting them by value". That's what STL did when forwarding references didn't exist. std::apply uses forwarding reference as counter-example.
– Jarod42
Jan 29 at 11:10
1
So, why would you copy when you can move? I totally don't get it.return [callable = std::move(callable)]() mutable {…}
– Arne Vogel
Jan 29 at 12:22
|
show 11 more comments
Since callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture. To prevent that, if an argument is an r-value it needs to be copied.
A working example:
template<class Callable>
auto discardable(Callable&& callable) { // This one makes a copy of the temporary.
return [callable = std::move(callable)]() mutable {
static_cast<void>(static_cast<Callable&&>(callable)());
};
}
template<class Callable>
auto discardable(Callable& callable) {
return [&callable]() mutable {
static_cast<void>(callable());
};
}
You can still face lifetime issues if callable
is an l-value reference but its lifetime scope is smaller than that of the lambda capture returned by discardable
. So, it may be the safest and easiest to always move or copy callable
.
As a side note, although there are new specialised utilities that perfect-forward the value category of the function object, like std::apply
, the standard library algorithms always copy function objects by accepting them by value. So that if one overloaded both operator()()&
and operator()()&&
the standard library would always use operator()()&
.
Since callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture. To prevent that, if an argument is an r-value it needs to be copied.
A working example:
template<class Callable>
auto discardable(Callable&& callable) { // This one makes a copy of the temporary.
return [callable = std::move(callable)]() mutable {
static_cast<void>(static_cast<Callable&&>(callable)());
};
}
template<class Callable>
auto discardable(Callable& callable) {
return [&callable]() mutable {
static_cast<void>(callable());
};
}
You can still face lifetime issues if callable
is an l-value reference but its lifetime scope is smaller than that of the lambda capture returned by discardable
. So, it may be the safest and easiest to always move or copy callable
.
As a side note, although there are new specialised utilities that perfect-forward the value category of the function object, like std::apply
, the standard library algorithms always copy function objects by accepting them by value. So that if one overloaded both operator()()&
and operator()()&&
the standard library would always use operator()()&
.
edited Jan 29 at 14:05
answered Jan 29 at 11:00
Maxim EgorushkinMaxim Egorushkin
87.9k11102186
87.9k11102186
Does it needstd::move
in the capture thingie? So as to avoid a copy if unnecessary? Genuine question; I am a little behind on the latest gadgets.
– Lightness Races in Orbit
Jan 29 at 11:01
2
@LightnessRacesinOrbit Someone may declareoperator()()&&
along withoperator()()&
. IMO, that is brittle, but conceivable. Although the standard library just makes copies of function objects and is not concerned with such trifles.
– Maxim Egorushkin
Jan 29 at 11:03
1
Just seems like a wasted copy for nothing. You can still invokeoperator()()&&
on it
– Lightness Races in Orbit
Jan 29 at 11:04
2
"the standard library always copies function objects by accepting them by value". That's what STL did when forwarding references didn't exist. std::apply uses forwarding reference as counter-example.
– Jarod42
Jan 29 at 11:10
1
So, why would you copy when you can move? I totally don't get it.return [callable = std::move(callable)]() mutable {…}
– Arne Vogel
Jan 29 at 12:22
|
show 11 more comments
Does it needstd::move
in the capture thingie? So as to avoid a copy if unnecessary? Genuine question; I am a little behind on the latest gadgets.
– Lightness Races in Orbit
Jan 29 at 11:01
2
@LightnessRacesinOrbit Someone may declareoperator()()&&
along withoperator()()&
. IMO, that is brittle, but conceivable. Although the standard library just makes copies of function objects and is not concerned with such trifles.
– Maxim Egorushkin
Jan 29 at 11:03
1
Just seems like a wasted copy for nothing. You can still invokeoperator()()&&
on it
– Lightness Races in Orbit
Jan 29 at 11:04
2
"the standard library always copies function objects by accepting them by value". That's what STL did when forwarding references didn't exist. std::apply uses forwarding reference as counter-example.
– Jarod42
Jan 29 at 11:10
1
So, why would you copy when you can move? I totally don't get it.return [callable = std::move(callable)]() mutable {…}
– Arne Vogel
Jan 29 at 12:22
Does it need
std::move
in the capture thingie? So as to avoid a copy if unnecessary? Genuine question; I am a little behind on the latest gadgets.– Lightness Races in Orbit
Jan 29 at 11:01
Does it need
std::move
in the capture thingie? So as to avoid a copy if unnecessary? Genuine question; I am a little behind on the latest gadgets.– Lightness Races in Orbit
Jan 29 at 11:01
2
2
@LightnessRacesinOrbit Someone may declare
operator()()&&
along with operator()()&
. IMO, that is brittle, but conceivable. Although the standard library just makes copies of function objects and is not concerned with such trifles.– Maxim Egorushkin
Jan 29 at 11:03
@LightnessRacesinOrbit Someone may declare
operator()()&&
along with operator()()&
. IMO, that is brittle, but conceivable. Although the standard library just makes copies of function objects and is not concerned with such trifles.– Maxim Egorushkin
Jan 29 at 11:03
1
1
Just seems like a wasted copy for nothing. You can still invoke
operator()()&&
on it– Lightness Races in Orbit
Jan 29 at 11:04
Just seems like a wasted copy for nothing. You can still invoke
operator()()&&
on it– Lightness Races in Orbit
Jan 29 at 11:04
2
2
"the standard library always copies function objects by accepting them by value". That's what STL did when forwarding references didn't exist. std::apply uses forwarding reference as counter-example.
– Jarod42
Jan 29 at 11:10
"the standard library always copies function objects by accepting them by value". That's what STL did when forwarding references didn't exist. std::apply uses forwarding reference as counter-example.
– Jarod42
Jan 29 at 11:10
1
1
So, why would you copy when you can move? I totally don't get it.
return [callable = std::move(callable)]() mutable {…}
– Arne Vogel
Jan 29 at 12:22
So, why would you copy when you can move? I totally don't get it.
return [callable = std::move(callable)]() mutable {…}
– Arne Vogel
Jan 29 at 12:22
|
show 11 more comments
Your program is UB as you use dangling reference of the captured lambda.
So to perfect forward capture in lambda, you may use
template<class Callable>
auto discardable(Callable&& callable)
{
return [f = std::conditional_t<
std::is_lvalue_reference<Callable>::value,
std::reference_wrapper<std::remove_reference_t<Callable>>,
Callable>{std::forward<Callable>(callable)}]
{
std::forward<Callable>(f)();
};
}
It move-constructs the temporary lambda.
1
Is forwarding captured variable really necessary?
– bartop
Jan 29 at 10:54
IMO you should not forwardf
since you may want to call the lambda multiple times.
– Holt
Jan 29 at 20:38
@Holt: Thatforward
/move
only changes category value of the functor. There is no assignment to transfer ownership.operator()()&&
might invalidate itself, but in that case, presence of this kind of overload might mean that user wants to respect category value.
– Jarod42
Jan 29 at 20:48
@Jarod42 I am talking aboutoperator()()&&
invalidatingf
as you mention. I don't know OP's intent, I'm just saying this might be worth mentioning.
– Holt
Jan 29 at 20:50
add a comment |
Your program is UB as you use dangling reference of the captured lambda.
So to perfect forward capture in lambda, you may use
template<class Callable>
auto discardable(Callable&& callable)
{
return [f = std::conditional_t<
std::is_lvalue_reference<Callable>::value,
std::reference_wrapper<std::remove_reference_t<Callable>>,
Callable>{std::forward<Callable>(callable)}]
{
std::forward<Callable>(f)();
};
}
It move-constructs the temporary lambda.
1
Is forwarding captured variable really necessary?
– bartop
Jan 29 at 10:54
IMO you should not forwardf
since you may want to call the lambda multiple times.
– Holt
Jan 29 at 20:38
@Holt: Thatforward
/move
only changes category value of the functor. There is no assignment to transfer ownership.operator()()&&
might invalidate itself, but in that case, presence of this kind of overload might mean that user wants to respect category value.
– Jarod42
Jan 29 at 20:48
@Jarod42 I am talking aboutoperator()()&&
invalidatingf
as you mention. I don't know OP's intent, I'm just saying this might be worth mentioning.
– Holt
Jan 29 at 20:50
add a comment |
Your program is UB as you use dangling reference of the captured lambda.
So to perfect forward capture in lambda, you may use
template<class Callable>
auto discardable(Callable&& callable)
{
return [f = std::conditional_t<
std::is_lvalue_reference<Callable>::value,
std::reference_wrapper<std::remove_reference_t<Callable>>,
Callable>{std::forward<Callable>(callable)}]
{
std::forward<Callable>(f)();
};
}
It move-constructs the temporary lambda.
Your program is UB as you use dangling reference of the captured lambda.
So to perfect forward capture in lambda, you may use
template<class Callable>
auto discardable(Callable&& callable)
{
return [f = std::conditional_t<
std::is_lvalue_reference<Callable>::value,
std::reference_wrapper<std::remove_reference_t<Callable>>,
Callable>{std::forward<Callable>(callable)}]
{
std::forward<Callable>(f)();
};
}
It move-constructs the temporary lambda.
edited Jan 29 at 14:37
answered Jan 29 at 10:51
Jarod42Jarod42
117k12103185
117k12103185
1
Is forwarding captured variable really necessary?
– bartop
Jan 29 at 10:54
IMO you should not forwardf
since you may want to call the lambda multiple times.
– Holt
Jan 29 at 20:38
@Holt: Thatforward
/move
only changes category value of the functor. There is no assignment to transfer ownership.operator()()&&
might invalidate itself, but in that case, presence of this kind of overload might mean that user wants to respect category value.
– Jarod42
Jan 29 at 20:48
@Jarod42 I am talking aboutoperator()()&&
invalidatingf
as you mention. I don't know OP's intent, I'm just saying this might be worth mentioning.
– Holt
Jan 29 at 20:50
add a comment |
1
Is forwarding captured variable really necessary?
– bartop
Jan 29 at 10:54
IMO you should not forwardf
since you may want to call the lambda multiple times.
– Holt
Jan 29 at 20:38
@Holt: Thatforward
/move
only changes category value of the functor. There is no assignment to transfer ownership.operator()()&&
might invalidate itself, but in that case, presence of this kind of overload might mean that user wants to respect category value.
– Jarod42
Jan 29 at 20:48
@Jarod42 I am talking aboutoperator()()&&
invalidatingf
as you mention. I don't know OP's intent, I'm just saying this might be worth mentioning.
– Holt
Jan 29 at 20:50
1
1
Is forwarding captured variable really necessary?
– bartop
Jan 29 at 10:54
Is forwarding captured variable really necessary?
– bartop
Jan 29 at 10:54
IMO you should not forward
f
since you may want to call the lambda multiple times.– Holt
Jan 29 at 20:38
IMO you should not forward
f
since you may want to call the lambda multiple times.– Holt
Jan 29 at 20:38
@Holt: That
forward
/move
only changes category value of the functor. There is no assignment to transfer ownership. operator()()&&
might invalidate itself, but in that case, presence of this kind of overload might mean that user wants to respect category value.– Jarod42
Jan 29 at 20:48
@Holt: That
forward
/move
only changes category value of the functor. There is no assignment to transfer ownership. operator()()&&
might invalidate itself, but in that case, presence of this kind of overload might mean that user wants to respect category value.– Jarod42
Jan 29 at 20:48
@Jarod42 I am talking about
operator()()&&
invalidating f
as you mention. I don't know OP's intent, I'm just saying this might be worth mentioning.– Holt
Jan 29 at 20:50
@Jarod42 I am talking about
operator()()&&
invalidating f
as you mention. I don't know OP's intent, I'm just saying this might be worth mentioning.– Holt
Jan 29 at 20:50
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.
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%2f54418941%2fperfect-forwarding-of-a-callable%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
4
Since
callable
can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture.– Maxim Egorushkin
Jan 29 at 10:47