Perfect forwarding of a callable












15















I came up with the following code to transform an 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.




  1. Is it well-defined?

  2. 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.










share|improve this question




















  • 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
    yesterday
















15















I came up with the following code to transform an 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.




  1. Is it well-defined?

  2. 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.










share|improve this question




















  • 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
    yesterday














15












15








15








I came up with the following code to transform an 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.




  1. Is it well-defined?

  2. 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.










share|improve this question
















I came up with the following code to transform an 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.




  1. Is it well-defined?

  2. 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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited yesterday









double-beep

2,1382824




2,1382824










asked yesterday









YSCYSC

22.1k351104




22.1k351104








  • 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
    yesterday














  • 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
    yesterday








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
yesterday





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
yesterday












3 Answers
3






active

oldest

votes


















13














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
};
}





share|improve this answer





















  • 1





    Are you sure forward and move should not be reversed?

    – Maxim Egorushkin
    yesterday











  • @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
    yesterday











  • @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
    yesterday













  • (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
    yesterday





















6














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()()&.






share|improve this answer


























  • 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
    yesterday






  • 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
    yesterday








  • 1





    Just seems like a wasted copy for nothing. You can still invoke operator()()&& on it

    – Lightness Races in Orbit
    yesterday








  • 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
    yesterday








  • 1





    So, why would you copy when you can move? I totally don't get it. return [callable = std::move(callable)]() mutable {…}

    – Arne Vogel
    yesterday





















5














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.






share|improve this answer





















  • 1





    Is forwarding captured variable really necessary?

    – bartop
    yesterday











  • IMO you should not forward f since you may want to call the lambda multiple times.

    – Holt
    yesterday











  • @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
    yesterday











  • @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
    yesterday













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
});


}
});














draft saved

draft discarded


















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









13














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
};
}





share|improve this answer





















  • 1





    Are you sure forward and move should not be reversed?

    – Maxim Egorushkin
    yesterday











  • @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
    yesterday











  • @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
    yesterday













  • (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
    yesterday


















13














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
};
}





share|improve this answer





















  • 1





    Are you sure forward and move should not be reversed?

    – Maxim Egorushkin
    yesterday











  • @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
    yesterday











  • @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
    yesterday













  • (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
    yesterday
















13












13








13







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
};
}





share|improve this answer















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
};
}






share|improve this answer














share|improve this answer



share|improve this answer








edited yesterday

























answered yesterday









Passer ByPasser By

9,41832455




9,41832455








  • 1





    Are you sure forward and move should not be reversed?

    – Maxim Egorushkin
    yesterday











  • @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
    yesterday











  • @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
    yesterday













  • (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
    yesterday
















  • 1





    Are you sure forward and move should not be reversed?

    – Maxim Egorushkin
    yesterday











  • @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
    yesterday











  • @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
    yesterday













  • (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
    yesterday










1




1





Are you sure forward and move should not be reversed?

– Maxim Egorushkin
yesterday





Are you sure forward and move should not be reversed?

– Maxim Egorushkin
yesterday













@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
yesterday





@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
yesterday













@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
yesterday







@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
yesterday















(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
yesterday







(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
yesterday















6














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()()&.






share|improve this answer


























  • 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
    yesterday






  • 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
    yesterday








  • 1





    Just seems like a wasted copy for nothing. You can still invoke operator()()&& on it

    – Lightness Races in Orbit
    yesterday








  • 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
    yesterday








  • 1





    So, why would you copy when you can move? I totally don't get it. return [callable = std::move(callable)]() mutable {…}

    – Arne Vogel
    yesterday


















6














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()()&.






share|improve this answer


























  • 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
    yesterday






  • 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
    yesterday








  • 1





    Just seems like a wasted copy for nothing. You can still invoke operator()()&& on it

    – Lightness Races in Orbit
    yesterday








  • 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
    yesterday








  • 1





    So, why would you copy when you can move? I totally don't get it. return [callable = std::move(callable)]() mutable {…}

    – Arne Vogel
    yesterday
















6












6








6







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()()&.






share|improve this answer















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()()&.







share|improve this answer














share|improve this answer



share|improve this answer








edited yesterday

























answered yesterday









Maxim EgorushkinMaxim Egorushkin

86.9k11101184




86.9k11101184













  • 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
    yesterday






  • 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
    yesterday








  • 1





    Just seems like a wasted copy for nothing. You can still invoke operator()()&& on it

    – Lightness Races in Orbit
    yesterday








  • 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
    yesterday








  • 1





    So, why would you copy when you can move? I totally don't get it. return [callable = std::move(callable)]() mutable {…}

    – Arne Vogel
    yesterday





















  • 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
    yesterday






  • 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
    yesterday








  • 1





    Just seems like a wasted copy for nothing. You can still invoke operator()()&& on it

    – Lightness Races in Orbit
    yesterday








  • 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
    yesterday








  • 1





    So, why would you copy when you can move? I totally don't get it. return [callable = std::move(callable)]() mutable {…}

    – Arne Vogel
    yesterday



















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
yesterday





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
yesterday




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
yesterday







@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
yesterday






1




1





Just seems like a wasted copy for nothing. You can still invoke operator()()&& on it

– Lightness Races in Orbit
yesterday







Just seems like a wasted copy for nothing. You can still invoke operator()()&& on it

– Lightness Races in Orbit
yesterday






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
yesterday







"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
yesterday






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
yesterday







So, why would you copy when you can move? I totally don't get it. return [callable = std::move(callable)]() mutable {…}

– Arne Vogel
yesterday













5














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.






share|improve this answer





















  • 1





    Is forwarding captured variable really necessary?

    – bartop
    yesterday











  • IMO you should not forward f since you may want to call the lambda multiple times.

    – Holt
    yesterday











  • @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
    yesterday











  • @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
    yesterday


















5














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.






share|improve this answer





















  • 1





    Is forwarding captured variable really necessary?

    – bartop
    yesterday











  • IMO you should not forward f since you may want to call the lambda multiple times.

    – Holt
    yesterday











  • @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
    yesterday











  • @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
    yesterday
















5












5








5







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.






share|improve this answer















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.







share|improve this answer














share|improve this answer



share|improve this answer








edited yesterday

























answered yesterday









Jarod42Jarod42

116k12102182




116k12102182








  • 1





    Is forwarding captured variable really necessary?

    – bartop
    yesterday











  • IMO you should not forward f since you may want to call the lambda multiple times.

    – Holt
    yesterday











  • @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
    yesterday











  • @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
    yesterday
















  • 1





    Is forwarding captured variable really necessary?

    – bartop
    yesterday











  • IMO you should not forward f since you may want to call the lambda multiple times.

    – Holt
    yesterday











  • @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
    yesterday











  • @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
    yesterday










1




1





Is forwarding captured variable really necessary?

– bartop
yesterday





Is forwarding captured variable really necessary?

– bartop
yesterday













IMO you should not forward f since you may want to call the lambda multiple times.

– Holt
yesterday





IMO you should not forward f since you may want to call the lambda multiple times.

– Holt
yesterday













@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
yesterday





@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
yesterday













@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
yesterday







@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
yesterday




















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

"Incorrect syntax near the keyword 'ON'. (on update cascade, on delete cascade,)

Alcedinidae

Origin of the phrase “under your belt”?