Why can't argument be forwarded inside lambda without mutable?Why can't variables be declared in a switch statement?Why are Python lambdas useful?Why does C++11's lambda require “mutable” keyword for capture-by-value, by default?Does lambda capture support variadic template argumentsCan't compile basic C++ program with TBB and lambdaCompiling with gcc fails if using lambda function for QObject::connect()Why std::bind cannot resolve function overloads with multiple arguments?Create shared packaged_task that accepts a parameter with forwardingIntel compiler failing to compile variadic lambda capture with multiple argumentsPassing variadic template to pthread_create

Do high-wing aircraft represent more difficult engineering challenges than low-wing aircraft?

Resistor Selection to retain same brightness in LED PWM circuit

FIFO data structure in pure C

Why doesn't Iron Man's action affect this person in Endgame?

Divisor Rich and Poor Numbers

Why do academics prefer Mac/Linux?

A latin word for "area of interest"

Solenoid fastest possible release - for how long should reversed polarity be applied?

What technology would Dwarves need to forge titanium?

Is there any deeper thematic meaning to the white horse that Arya finds in The Bells (S08E05)?

Single word that parallels "Recent" when discussing the near future

Is it standard for US-based universities to consider the ethnicity of an applicant during PhD admissions?

Can I pay my credit card?

What formula to chose a nonlinear formula?

Write electromagnetic field tensor in terms of four-vector potential

Why is Drogon so much better in battle than Rhaegal and Viserion?

How can we delete item permanently without storing in Recycle Bin?

How was the blinking terminal cursor invented?

Is Precocious Apprentice enough for Mystic Theurge?

How to know the path of a particular software?

How to pass store code to custom URL in magento 2

Do we see some Unsullied doing this in S08E05?

How does the Heat Metal spell interact with a follow-up Frostbite spell?

Was the dragon prowess intentionally downplayed in S08E04?



Why can't argument be forwarded inside lambda without mutable?


Why can't variables be declared in a switch statement?Why are Python lambdas useful?Why does C++11's lambda require “mutable” keyword for capture-by-value, by default?Does lambda capture support variadic template argumentsCan't compile basic C++ program with TBB and lambdaCompiling with gcc fails if using lambda function for QObject::connect()Why std::bind cannot resolve function overloads with multiple arguments?Create shared packaged_task that accepts a parameter with forwardingIntel compiler failing to compile variadic lambda capture with multiple argumentsPassing variadic template to pthread_create






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








15















In the below program, when mutable is not used, the program fails to compile.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
//q.emplace([=]() // this fails
q.emplace([=]() mutable //this works
func(std::forward<Args>(args)...);
);


int main()

auto f1 = [](int a, int b) std::cout << a << b << "n"; ;
auto f2 = [](double a, double b) std::cout << a << b << "n";;
enqueue(f1, 10, 20);
enqueue(f2, 3.14, 2.14);
return 0;



This is the compiler error



lmbfwd.cpp: In instantiation of ‘enqueue(T&&, Args&& ...)::<lambda()> [with T = main()::<lambda(int, int)>&; Args = int, int]’:
lmbfwd.cpp:11:27: required from ‘struct enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]::<lambda()>’
lmbfwd.cpp:10:2: required from ‘void enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]’
lmbfwd.cpp:18:20: required from here
lmbfwd.cpp:11:26: error: no matching function for call to ‘forward<int>(const int&)’
func(std::forward<Args>(args)...);


I am not able to understand why argument forwarding fails without mutable.



Besides, if I pass a lambda with string as argument, mutable is not required and program works.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

//works without mutable
q.emplace([=]()
func(std::forward<Args>(args)...);
);

void dequeue()

while (!q.empty())
auto f = std::move(q.front());
q.pop();
f();


int main()

auto f3 = [](std::string s) std::cout << s << "n"; ;
enqueue(f3, "Hello");
dequeue();
return 0;



Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?










share|improve this question



















  • 2





    As an aside, in the second example you are not forwarding std::string, you are forwarding const char*. It doesn't matter what type the function takes, but what type arguments are passed to enqueue. My guess is, the argument being const from the start somehow makes a difference, but I'm not sure how.

    – Igor Tandetnik
    May 5 at 14:23






  • 4





    And indeed, it does fail if you make it enqueue(f3, std::string("Hello"));

    – Igor Tandetnik
    May 5 at 14:24






  • 1





    ...yes, or "Hello"s (using string literals)

    – andreee
    May 5 at 14:25











  • @IgorTandetnik The direction is right: if you pass a const argument, the error disappears. See for example godbolt.org/z/EdY4Mn where I just introduced some empty class Foo. If you remove the constness of Foo, it fails to compile. Still not sure why, though...

    – andreee
    May 5 at 14:29












  • It may be enlightening (although pointless) to realize that using std::forward<const Args> also fixes the compile error.

    – Vaughn Cato
    May 5 at 17:14

















15















In the below program, when mutable is not used, the program fails to compile.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
//q.emplace([=]() // this fails
q.emplace([=]() mutable //this works
func(std::forward<Args>(args)...);
);


int main()

auto f1 = [](int a, int b) std::cout << a << b << "n"; ;
auto f2 = [](double a, double b) std::cout << a << b << "n";;
enqueue(f1, 10, 20);
enqueue(f2, 3.14, 2.14);
return 0;



This is the compiler error



lmbfwd.cpp: In instantiation of ‘enqueue(T&&, Args&& ...)::<lambda()> [with T = main()::<lambda(int, int)>&; Args = int, int]’:
lmbfwd.cpp:11:27: required from ‘struct enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]::<lambda()>’
lmbfwd.cpp:10:2: required from ‘void enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]’
lmbfwd.cpp:18:20: required from here
lmbfwd.cpp:11:26: error: no matching function for call to ‘forward<int>(const int&)’
func(std::forward<Args>(args)...);


I am not able to understand why argument forwarding fails without mutable.



Besides, if I pass a lambda with string as argument, mutable is not required and program works.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

//works without mutable
q.emplace([=]()
func(std::forward<Args>(args)...);
);

void dequeue()

while (!q.empty())
auto f = std::move(q.front());
q.pop();
f();


int main()

auto f3 = [](std::string s) std::cout << s << "n"; ;
enqueue(f3, "Hello");
dequeue();
return 0;



Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?










share|improve this question



















  • 2





    As an aside, in the second example you are not forwarding std::string, you are forwarding const char*. It doesn't matter what type the function takes, but what type arguments are passed to enqueue. My guess is, the argument being const from the start somehow makes a difference, but I'm not sure how.

    – Igor Tandetnik
    May 5 at 14:23






  • 4





    And indeed, it does fail if you make it enqueue(f3, std::string("Hello"));

    – Igor Tandetnik
    May 5 at 14:24






  • 1





    ...yes, or "Hello"s (using string literals)

    – andreee
    May 5 at 14:25











  • @IgorTandetnik The direction is right: if you pass a const argument, the error disappears. See for example godbolt.org/z/EdY4Mn where I just introduced some empty class Foo. If you remove the constness of Foo, it fails to compile. Still not sure why, though...

    – andreee
    May 5 at 14:29












  • It may be enlightening (although pointless) to realize that using std::forward<const Args> also fixes the compile error.

    – Vaughn Cato
    May 5 at 17:14













15












15








15


3






In the below program, when mutable is not used, the program fails to compile.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
//q.emplace([=]() // this fails
q.emplace([=]() mutable //this works
func(std::forward<Args>(args)...);
);


int main()

auto f1 = [](int a, int b) std::cout << a << b << "n"; ;
auto f2 = [](double a, double b) std::cout << a << b << "n";;
enqueue(f1, 10, 20);
enqueue(f2, 3.14, 2.14);
return 0;



This is the compiler error



lmbfwd.cpp: In instantiation of ‘enqueue(T&&, Args&& ...)::<lambda()> [with T = main()::<lambda(int, int)>&; Args = int, int]’:
lmbfwd.cpp:11:27: required from ‘struct enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]::<lambda()>’
lmbfwd.cpp:10:2: required from ‘void enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]’
lmbfwd.cpp:18:20: required from here
lmbfwd.cpp:11:26: error: no matching function for call to ‘forward<int>(const int&)’
func(std::forward<Args>(args)...);


I am not able to understand why argument forwarding fails without mutable.



Besides, if I pass a lambda with string as argument, mutable is not required and program works.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

//works without mutable
q.emplace([=]()
func(std::forward<Args>(args)...);
);

void dequeue()

while (!q.empty())
auto f = std::move(q.front());
q.pop();
f();


int main()

auto f3 = [](std::string s) std::cout << s << "n"; ;
enqueue(f3, "Hello");
dequeue();
return 0;



Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?










share|improve this question
















In the below program, when mutable is not used, the program fails to compile.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
//q.emplace([=]() // this fails
q.emplace([=]() mutable //this works
func(std::forward<Args>(args)...);
);


int main()

auto f1 = [](int a, int b) std::cout << a << b << "n"; ;
auto f2 = [](double a, double b) std::cout << a << b << "n";;
enqueue(f1, 10, 20);
enqueue(f2, 3.14, 2.14);
return 0;



This is the compiler error



lmbfwd.cpp: In instantiation of ‘enqueue(T&&, Args&& ...)::<lambda()> [with T = main()::<lambda(int, int)>&; Args = int, int]’:
lmbfwd.cpp:11:27: required from ‘struct enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]::<lambda()>’
lmbfwd.cpp:10:2: required from ‘void enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = int, int]’
lmbfwd.cpp:18:20: required from here
lmbfwd.cpp:11:26: error: no matching function for call to ‘forward<int>(const int&)’
func(std::forward<Args>(args)...);


I am not able to understand why argument forwarding fails without mutable.



Besides, if I pass a lambda with string as argument, mutable is not required and program works.



#include <iostream>
#include <queue>
#include <functional>

std::queue<std::function<void()>> q;

template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

//works without mutable
q.emplace([=]()
func(std::forward<Args>(args)...);
);

void dequeue()

while (!q.empty())
auto f = std::move(q.front());
q.pop();
f();


int main()

auto f3 = [](std::string s) std::cout << s << "n"; ;
enqueue(f3, "Hello");
dequeue();
return 0;



Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?







c++ c++11 lambda language-lawyer






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited May 5 at 14:26









Swordfish

11.8k21539




11.8k21539










asked May 5 at 14:11









bornfreebornfree

8731923




8731923







  • 2





    As an aside, in the second example you are not forwarding std::string, you are forwarding const char*. It doesn't matter what type the function takes, but what type arguments are passed to enqueue. My guess is, the argument being const from the start somehow makes a difference, but I'm not sure how.

    – Igor Tandetnik
    May 5 at 14:23






  • 4





    And indeed, it does fail if you make it enqueue(f3, std::string("Hello"));

    – Igor Tandetnik
    May 5 at 14:24






  • 1





    ...yes, or "Hello"s (using string literals)

    – andreee
    May 5 at 14:25











  • @IgorTandetnik The direction is right: if you pass a const argument, the error disappears. See for example godbolt.org/z/EdY4Mn where I just introduced some empty class Foo. If you remove the constness of Foo, it fails to compile. Still not sure why, though...

    – andreee
    May 5 at 14:29












  • It may be enlightening (although pointless) to realize that using std::forward<const Args> also fixes the compile error.

    – Vaughn Cato
    May 5 at 17:14












  • 2





    As an aside, in the second example you are not forwarding std::string, you are forwarding const char*. It doesn't matter what type the function takes, but what type arguments are passed to enqueue. My guess is, the argument being const from the start somehow makes a difference, but I'm not sure how.

    – Igor Tandetnik
    May 5 at 14:23






  • 4





    And indeed, it does fail if you make it enqueue(f3, std::string("Hello"));

    – Igor Tandetnik
    May 5 at 14:24






  • 1





    ...yes, or "Hello"s (using string literals)

    – andreee
    May 5 at 14:25











  • @IgorTandetnik The direction is right: if you pass a const argument, the error disappears. See for example godbolt.org/z/EdY4Mn where I just introduced some empty class Foo. If you remove the constness of Foo, it fails to compile. Still not sure why, though...

    – andreee
    May 5 at 14:29












  • It may be enlightening (although pointless) to realize that using std::forward<const Args> also fixes the compile error.

    – Vaughn Cato
    May 5 at 17:14







2




2





As an aside, in the second example you are not forwarding std::string, you are forwarding const char*. It doesn't matter what type the function takes, but what type arguments are passed to enqueue. My guess is, the argument being const from the start somehow makes a difference, but I'm not sure how.

– Igor Tandetnik
May 5 at 14:23





As an aside, in the second example you are not forwarding std::string, you are forwarding const char*. It doesn't matter what type the function takes, but what type arguments are passed to enqueue. My guess is, the argument being const from the start somehow makes a difference, but I'm not sure how.

– Igor Tandetnik
May 5 at 14:23




4




4





And indeed, it does fail if you make it enqueue(f3, std::string("Hello"));

– Igor Tandetnik
May 5 at 14:24





And indeed, it does fail if you make it enqueue(f3, std::string("Hello"));

– Igor Tandetnik
May 5 at 14:24




1




1





...yes, or "Hello"s (using string literals)

– andreee
May 5 at 14:25





...yes, or "Hello"s (using string literals)

– andreee
May 5 at 14:25













@IgorTandetnik The direction is right: if you pass a const argument, the error disappears. See for example godbolt.org/z/EdY4Mn where I just introduced some empty class Foo. If you remove the constness of Foo, it fails to compile. Still not sure why, though...

– andreee
May 5 at 14:29






@IgorTandetnik The direction is right: if you pass a const argument, the error disappears. See for example godbolt.org/z/EdY4Mn where I just introduced some empty class Foo. If you remove the constness of Foo, it fails to compile. Still not sure why, though...

– andreee
May 5 at 14:29














It may be enlightening (although pointless) to realize that using std::forward<const Args> also fixes the compile error.

– Vaughn Cato
May 5 at 17:14





It may be enlightening (although pointless) to realize that using std::forward<const Args> also fixes the compile error.

– Vaughn Cato
May 5 at 17:14












1 Answer
1






active

oldest

votes


















18














A non-mutable lambda generates a closure type with an implicit const qualifier on its operator() overload.



std::forward is a conditional move: it is equivalent to std::move when the provided template argument is not an lvalue reference. It is defined as follows:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;

template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


(See: https://en.cppreference.com/w/cpp/utility/forward).




Let's simplify your snippet to:



#include <utility>

template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[=] func(std::forward<Args>(args)...); ;


int main()

enqueue([](int) , 10);



The error produced by clang++ 8.x is:




error: no matching function for call to 'forward'
[=] func(std::forward<Args>(args)...); ;
^~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization 'enqueue<(lambda at wtf.cpp:11:13), int>' requested here
enqueue([](int) , 10);
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^



In the snippet above:



  • Args is int and refers to the type outside of the lambda.


  • args refers to the member of the closure synthesized via lambda capture, and is const due to the lack of mutable.


Therefore the std::forward invocation is...



std::forward<int>(/* `const int&` member of closure */)


...which doesn't match any existing overload of std::forward. There is a mismatch between the template argument provided to forward and its function argument type.



Adding mutable to the lambda makes args non-const, and a suitable forward overload is found (the first one, which moves its argument).




By using C++20 pack-expansion captures to "rewrite" the name of args, we can avoid the mismatch mentioned above, making the code compile even without mutable:



template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[func, ...xs = args] func(std::forward<decltype(xs)>(xs)...); ;



live example on godbolt.org





Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?




This is a fun one - it works because you're not actually passing a std::string in your invocation:



enqueue(f3, "Hello");
// ^~~~~~~
// const char*


If you correctly match the type of the argument passed to enqueue to the one accepted by f3, it will stop working as expected (unless you use mutable or C++20 features):



enqueue(f3, std::string"Hello");
// Compile-time error.



To explain why the version with const char* works, let's again look at a simplified example:



template <typename T>
void enqueue(T&& func, const char (&arg)[6])

[=] func(std::forward<const char*>(arg)); ;


int main()

enqueue([](std::string) , "Hello");



Args is deduced as const char(&)[6]. There is a matching forward overload:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


After substitution:



template< class T >
constexpr const char*&& forward( const char*&& t ) noexcept;


This simply returns t, which is then used to construct the std::string.






share|improve this answer




















  • 2





    Wow, that's an excellent answer! Can you perhaps add some references to the stardard?

    – andreee
    May 5 at 14:35






  • 2





    @andreee: improved my answer, let me know if it is clear.

    – Vittorio Romeo
    May 5 at 14:45











  • Thanks for the explanation. I have a doubt, in case of const char *, we also have a const args.. still it works.. why is that ?

    – bornfree
    May 5 at 14:59






  • 2





    @bornfree: added an explanation.

    – Vittorio Romeo
    May 5 at 15:14






  • 1





    Where does const char* come from? He's passing around a reference so Args is const char(&)[6] so it's std::forward<const char(&)[6]>(arg).

    – 0x499602D2
    May 5 at 17:15











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%2f55992834%2fwhy-cant-argument-be-forwarded-inside-lambda-without-mutable%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









18














A non-mutable lambda generates a closure type with an implicit const qualifier on its operator() overload.



std::forward is a conditional move: it is equivalent to std::move when the provided template argument is not an lvalue reference. It is defined as follows:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;

template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


(See: https://en.cppreference.com/w/cpp/utility/forward).




Let's simplify your snippet to:



#include <utility>

template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[=] func(std::forward<Args>(args)...); ;


int main()

enqueue([](int) , 10);



The error produced by clang++ 8.x is:




error: no matching function for call to 'forward'
[=] func(std::forward<Args>(args)...); ;
^~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization 'enqueue<(lambda at wtf.cpp:11:13), int>' requested here
enqueue([](int) , 10);
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^



In the snippet above:



  • Args is int and refers to the type outside of the lambda.


  • args refers to the member of the closure synthesized via lambda capture, and is const due to the lack of mutable.


Therefore the std::forward invocation is...



std::forward<int>(/* `const int&` member of closure */)


...which doesn't match any existing overload of std::forward. There is a mismatch between the template argument provided to forward and its function argument type.



Adding mutable to the lambda makes args non-const, and a suitable forward overload is found (the first one, which moves its argument).




By using C++20 pack-expansion captures to "rewrite" the name of args, we can avoid the mismatch mentioned above, making the code compile even without mutable:



template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[func, ...xs = args] func(std::forward<decltype(xs)>(xs)...); ;



live example on godbolt.org





Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?




This is a fun one - it works because you're not actually passing a std::string in your invocation:



enqueue(f3, "Hello");
// ^~~~~~~
// const char*


If you correctly match the type of the argument passed to enqueue to the one accepted by f3, it will stop working as expected (unless you use mutable or C++20 features):



enqueue(f3, std::string"Hello");
// Compile-time error.



To explain why the version with const char* works, let's again look at a simplified example:



template <typename T>
void enqueue(T&& func, const char (&arg)[6])

[=] func(std::forward<const char*>(arg)); ;


int main()

enqueue([](std::string) , "Hello");



Args is deduced as const char(&)[6]. There is a matching forward overload:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


After substitution:



template< class T >
constexpr const char*&& forward( const char*&& t ) noexcept;


This simply returns t, which is then used to construct the std::string.






share|improve this answer




















  • 2





    Wow, that's an excellent answer! Can you perhaps add some references to the stardard?

    – andreee
    May 5 at 14:35






  • 2





    @andreee: improved my answer, let me know if it is clear.

    – Vittorio Romeo
    May 5 at 14:45











  • Thanks for the explanation. I have a doubt, in case of const char *, we also have a const args.. still it works.. why is that ?

    – bornfree
    May 5 at 14:59






  • 2





    @bornfree: added an explanation.

    – Vittorio Romeo
    May 5 at 15:14






  • 1





    Where does const char* come from? He's passing around a reference so Args is const char(&)[6] so it's std::forward<const char(&)[6]>(arg).

    – 0x499602D2
    May 5 at 17:15















18














A non-mutable lambda generates a closure type with an implicit const qualifier on its operator() overload.



std::forward is a conditional move: it is equivalent to std::move when the provided template argument is not an lvalue reference. It is defined as follows:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;

template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


(See: https://en.cppreference.com/w/cpp/utility/forward).




Let's simplify your snippet to:



#include <utility>

template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[=] func(std::forward<Args>(args)...); ;


int main()

enqueue([](int) , 10);



The error produced by clang++ 8.x is:




error: no matching function for call to 'forward'
[=] func(std::forward<Args>(args)...); ;
^~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization 'enqueue<(lambda at wtf.cpp:11:13), int>' requested here
enqueue([](int) , 10);
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^



In the snippet above:



  • Args is int and refers to the type outside of the lambda.


  • args refers to the member of the closure synthesized via lambda capture, and is const due to the lack of mutable.


Therefore the std::forward invocation is...



std::forward<int>(/* `const int&` member of closure */)


...which doesn't match any existing overload of std::forward. There is a mismatch between the template argument provided to forward and its function argument type.



Adding mutable to the lambda makes args non-const, and a suitable forward overload is found (the first one, which moves its argument).




By using C++20 pack-expansion captures to "rewrite" the name of args, we can avoid the mismatch mentioned above, making the code compile even without mutable:



template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[func, ...xs = args] func(std::forward<decltype(xs)>(xs)...); ;



live example on godbolt.org





Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?




This is a fun one - it works because you're not actually passing a std::string in your invocation:



enqueue(f3, "Hello");
// ^~~~~~~
// const char*


If you correctly match the type of the argument passed to enqueue to the one accepted by f3, it will stop working as expected (unless you use mutable or C++20 features):



enqueue(f3, std::string"Hello");
// Compile-time error.



To explain why the version with const char* works, let's again look at a simplified example:



template <typename T>
void enqueue(T&& func, const char (&arg)[6])

[=] func(std::forward<const char*>(arg)); ;


int main()

enqueue([](std::string) , "Hello");



Args is deduced as const char(&)[6]. There is a matching forward overload:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


After substitution:



template< class T >
constexpr const char*&& forward( const char*&& t ) noexcept;


This simply returns t, which is then used to construct the std::string.






share|improve this answer




















  • 2





    Wow, that's an excellent answer! Can you perhaps add some references to the stardard?

    – andreee
    May 5 at 14:35






  • 2





    @andreee: improved my answer, let me know if it is clear.

    – Vittorio Romeo
    May 5 at 14:45











  • Thanks for the explanation. I have a doubt, in case of const char *, we also have a const args.. still it works.. why is that ?

    – bornfree
    May 5 at 14:59






  • 2





    @bornfree: added an explanation.

    – Vittorio Romeo
    May 5 at 15:14






  • 1





    Where does const char* come from? He's passing around a reference so Args is const char(&)[6] so it's std::forward<const char(&)[6]>(arg).

    – 0x499602D2
    May 5 at 17:15













18












18








18







A non-mutable lambda generates a closure type with an implicit const qualifier on its operator() overload.



std::forward is a conditional move: it is equivalent to std::move when the provided template argument is not an lvalue reference. It is defined as follows:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;

template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


(See: https://en.cppreference.com/w/cpp/utility/forward).




Let's simplify your snippet to:



#include <utility>

template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[=] func(std::forward<Args>(args)...); ;


int main()

enqueue([](int) , 10);



The error produced by clang++ 8.x is:




error: no matching function for call to 'forward'
[=] func(std::forward<Args>(args)...); ;
^~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization 'enqueue<(lambda at wtf.cpp:11:13), int>' requested here
enqueue([](int) , 10);
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^



In the snippet above:



  • Args is int and refers to the type outside of the lambda.


  • args refers to the member of the closure synthesized via lambda capture, and is const due to the lack of mutable.


Therefore the std::forward invocation is...



std::forward<int>(/* `const int&` member of closure */)


...which doesn't match any existing overload of std::forward. There is a mismatch between the template argument provided to forward and its function argument type.



Adding mutable to the lambda makes args non-const, and a suitable forward overload is found (the first one, which moves its argument).




By using C++20 pack-expansion captures to "rewrite" the name of args, we can avoid the mismatch mentioned above, making the code compile even without mutable:



template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[func, ...xs = args] func(std::forward<decltype(xs)>(xs)...); ;



live example on godbolt.org





Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?




This is a fun one - it works because you're not actually passing a std::string in your invocation:



enqueue(f3, "Hello");
// ^~~~~~~
// const char*


If you correctly match the type of the argument passed to enqueue to the one accepted by f3, it will stop working as expected (unless you use mutable or C++20 features):



enqueue(f3, std::string"Hello");
// Compile-time error.



To explain why the version with const char* works, let's again look at a simplified example:



template <typename T>
void enqueue(T&& func, const char (&arg)[6])

[=] func(std::forward<const char*>(arg)); ;


int main()

enqueue([](std::string) , "Hello");



Args is deduced as const char(&)[6]. There is a matching forward overload:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


After substitution:



template< class T >
constexpr const char*&& forward( const char*&& t ) noexcept;


This simply returns t, which is then used to construct the std::string.






share|improve this answer















A non-mutable lambda generates a closure type with an implicit const qualifier on its operator() overload.



std::forward is a conditional move: it is equivalent to std::move when the provided template argument is not an lvalue reference. It is defined as follows:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;

template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


(See: https://en.cppreference.com/w/cpp/utility/forward).




Let's simplify your snippet to:



#include <utility>

template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[=] func(std::forward<Args>(args)...); ;


int main()

enqueue([](int) , 10);



The error produced by clang++ 8.x is:




error: no matching function for call to 'forward'
[=] func(std::forward<Args>(args)...); ;
^~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization 'enqueue<(lambda at wtf.cpp:11:13), int>' requested here
enqueue([](int) , 10);
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^



In the snippet above:



  • Args is int and refers to the type outside of the lambda.


  • args refers to the member of the closure synthesized via lambda capture, and is const due to the lack of mutable.


Therefore the std::forward invocation is...



std::forward<int>(/* `const int&` member of closure */)


...which doesn't match any existing overload of std::forward. There is a mismatch between the template argument provided to forward and its function argument type.



Adding mutable to the lambda makes args non-const, and a suitable forward overload is found (the first one, which moves its argument).




By using C++20 pack-expansion captures to "rewrite" the name of args, we can avoid the mismatch mentioned above, making the code compile even without mutable:



template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)

[func, ...xs = args] func(std::forward<decltype(xs)>(xs)...); ;



live example on godbolt.org





Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?




This is a fun one - it works because you're not actually passing a std::string in your invocation:



enqueue(f3, "Hello");
// ^~~~~~~
// const char*


If you correctly match the type of the argument passed to enqueue to the one accepted by f3, it will stop working as expected (unless you use mutable or C++20 features):



enqueue(f3, std::string"Hello");
// Compile-time error.



To explain why the version with const char* works, let's again look at a simplified example:



template <typename T>
void enqueue(T&& func, const char (&arg)[6])

[=] func(std::forward<const char*>(arg)); ;


int main()

enqueue([](std::string) , "Hello");



Args is deduced as const char(&)[6]. There is a matching forward overload:



template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;


After substitution:



template< class T >
constexpr const char*&& forward( const char*&& t ) noexcept;


This simply returns t, which is then used to construct the std::string.







share|improve this answer














share|improve this answer



share|improve this answer








edited May 5 at 15:14

























answered May 5 at 14:32









Vittorio RomeoVittorio Romeo

60.9k17170315




60.9k17170315







  • 2





    Wow, that's an excellent answer! Can you perhaps add some references to the stardard?

    – andreee
    May 5 at 14:35






  • 2





    @andreee: improved my answer, let me know if it is clear.

    – Vittorio Romeo
    May 5 at 14:45











  • Thanks for the explanation. I have a doubt, in case of const char *, we also have a const args.. still it works.. why is that ?

    – bornfree
    May 5 at 14:59






  • 2





    @bornfree: added an explanation.

    – Vittorio Romeo
    May 5 at 15:14






  • 1





    Where does const char* come from? He's passing around a reference so Args is const char(&)[6] so it's std::forward<const char(&)[6]>(arg).

    – 0x499602D2
    May 5 at 17:15












  • 2





    Wow, that's an excellent answer! Can you perhaps add some references to the stardard?

    – andreee
    May 5 at 14:35






  • 2





    @andreee: improved my answer, let me know if it is clear.

    – Vittorio Romeo
    May 5 at 14:45











  • Thanks for the explanation. I have a doubt, in case of const char *, we also have a const args.. still it works.. why is that ?

    – bornfree
    May 5 at 14:59






  • 2





    @bornfree: added an explanation.

    – Vittorio Romeo
    May 5 at 15:14






  • 1





    Where does const char* come from? He's passing around a reference so Args is const char(&)[6] so it's std::forward<const char(&)[6]>(arg).

    – 0x499602D2
    May 5 at 17:15







2




2





Wow, that's an excellent answer! Can you perhaps add some references to the stardard?

– andreee
May 5 at 14:35





Wow, that's an excellent answer! Can you perhaps add some references to the stardard?

– andreee
May 5 at 14:35




2




2





@andreee: improved my answer, let me know if it is clear.

– Vittorio Romeo
May 5 at 14:45





@andreee: improved my answer, let me know if it is clear.

– Vittorio Romeo
May 5 at 14:45













Thanks for the explanation. I have a doubt, in case of const char *, we also have a const args.. still it works.. why is that ?

– bornfree
May 5 at 14:59





Thanks for the explanation. I have a doubt, in case of const char *, we also have a const args.. still it works.. why is that ?

– bornfree
May 5 at 14:59




2




2





@bornfree: added an explanation.

– Vittorio Romeo
May 5 at 15:14





@bornfree: added an explanation.

– Vittorio Romeo
May 5 at 15:14




1




1





Where does const char* come from? He's passing around a reference so Args is const char(&)[6] so it's std::forward<const char(&)[6]>(arg).

– 0x499602D2
May 5 at 17:15





Where does const char* come from? He's passing around a reference so Args is const char(&)[6] so it's std::forward<const char(&)[6]>(arg).

– 0x499602D2
May 5 at 17:15



















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%2f55992834%2fwhy-cant-argument-be-forwarded-inside-lambda-without-mutable%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

Wikipedia:Vital articles Мазмуну Biography - Өмүр баян Philosophy and psychology - Философия жана психология Religion - Дин Social sciences - Коомдук илимдер Language and literature - Тил жана адабият Science - Илим Technology - Технология Arts and recreation - Искусство жана эс алуу History and geography - Тарых жана география Навигация менюсу

Bruxelas-Capital Índice Historia | Composición | Situación lingüística | Clima | Cidades irmandadas | Notas | Véxase tamén | Menú de navegacióneO uso das linguas en Bruxelas e a situación do neerlandés"Rexión de Bruxelas Capital"o orixinalSitio da rexiónPáxina de Bruselas no sitio da Oficina de Promoción Turística de Valonia e BruxelasMapa Interactivo da Rexión de Bruxelas-CapitaleeWorldCat332144929079854441105155190212ID28008674080552-90000 0001 0666 3698n94104302ID540940339365017018237

What should I write in an apology letter, since I have decided not to join a company after accepting an offer letterShould I keep looking after accepting a job offer?What should I do when I've been verbally told I would get an offer letter, but still haven't gotten one after 4 weeks?Do I accept an offer from a company that I am not likely to join?New job hasn't confirmed starting date and I want to give current employer as much notice as possibleHow should I address my manager in my resignation letter?HR delayed background verification, now jobless as resignedNo email communication after accepting a formal written offer. How should I phrase the call?What should I do if after receiving a verbal offer letter I am informed that my written job offer is put on hold due to some internal issues?Should I inform the current employer that I am about to resign within 1-2 weeks since I have signed the offer letter and waiting for visa?What company will do, if I send their offer letter to another company