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

Club Baloncesto Breogán Índice Historia | Pavillón | Nome | O Breogán na cultura popular | Xogadores | Adestradores | Presidentes | Palmarés | Historial | Líderes | Notas | Véxase tamén | Menú de navegacióncbbreogan.galCadroGuía oficial da ACB 2009-10, páxina 201Guía oficial ACB 1992, páxina 183. Editorial DB.É de 6.500 espectadores sentados axeitándose á última normativa"Estudiantes Junior, entre as mellores canteiras"o orixinalHemeroteca El Mundo Deportivo, 16 setembro de 1970, páxina 12Historia do BreogánAlfredo Pérez, o último canoneiroHistoria C.B. BreogánHemeroteca de El Mundo DeportivoJimmy Wright, norteamericano do Breogán deixará Lugo por ameazas de morteResultados de Breogán en 1986-87Resultados de Breogán en 1990-91Ficha de Velimir Perasović en acb.comResultados de Breogán en 1994-95Breogán arrasa al Barça. "El Mundo Deportivo", 27 de setembro de 1999, páxina 58CB Breogán - FC BarcelonaA FEB invita a participar nunha nova Liga EuropeaCharlie Bell na prensa estatalMáximos anotadores 2005Tempada 2005-06 : Tódolos Xogadores da Xornada""Non quero pensar nunha man negra, mais pregúntome que está a pasar""o orixinalRaúl López, orgulloso dos xogadores, presume da boa saúde económica do BreogánJulio González confirma que cesa como presidente del BreogánHomenaxe a Lisardo GómezA tempada do rexurdimento celesteEntrevista a Lisardo GómezEl COB dinamita el Pazo para forzar el quinto (69-73)Cafés Candelas, patrocinador del CB Breogán"Suso Lázare, novo presidente do Breogán"o orixinalCafés Candelas Breogán firma el mayor triunfo de la historiaEl Breogán realizará 17 homenajes por su cincuenta aniversario"O Breogán honra ao seu fundador e primeiro presidente"o orixinalMiguel Giao recibiu a homenaxe do PazoHomenaxe aos primeiros gladiadores celestesO home que nos amosa como ver o Breo co corazónTita Franco será homenaxeada polos #50anosdeBreoJulio Vila recibirá unha homenaxe in memoriam polos #50anosdeBreo"O Breogán homenaxeará aos seus aboados máis veteráns"Pechada ovación a «Capi» Sanmartín e Ricardo «Corazón de González»Homenaxe por décadas de informaciónPaco García volve ao Pazo con motivo do 50 aniversario"Resultados y clasificaciones""O Cafés Candelas Breogán, campión da Copa Princesa""O Cafés Candelas Breogán, equipo ACB"C.B. Breogán"Proxecto social"o orixinal"Centros asociados"o orixinalFicha en imdb.comMario Camus trata la recuperación del amor en 'La vieja música', su última película"Páxina web oficial""Club Baloncesto Breogán""C. B. Breogán S.A.D."eehttp://www.fegaba.com

Vilaño, A Laracha Índice Patrimonio | Lugares e parroquias | Véxase tamén | Menú de navegación43°14′52″N 8°36′03″O / 43.24775, -8.60070

Cegueira Índice Epidemioloxía | Deficiencia visual | Tipos de cegueira | Principais causas de cegueira | Tratamento | Técnicas de adaptación e axudas | Vida dos cegos | Primeiros auxilios | Crenzas respecto das persoas cegas | Crenzas das persoas cegas | O neno deficiente visual | Aspectos psicolóxicos da cegueira | Notas | Véxase tamén | Menú de navegación54.054.154.436928256blindnessDicionario da Real Academia GalegaPortal das Palabras"International Standards: Visual Standards — Aspects and Ranges of Vision Loss with Emphasis on Population Surveys.""Visual impairment and blindness""Presentan un plan para previr a cegueira"o orixinalACCDV Associació Catalana de Cecs i Disminuïts Visuals - PMFTrachoma"Effect of gene therapy on visual function in Leber's congenital amaurosis"1844137110.1056/NEJMoa0802268Cans guía - os mellores amigos dos cegosArquivadoEscola de cans guía para cegos en Mortágua, PortugalArquivado"Tecnología para ciegos y deficientes visuales. Recopilación de recursos gratuitos en la Red""Colorino""‘COL.diesis’, escuchar los sonidos del color""COL.diesis: Transforming Colour into Melody and Implementing the Result in a Colour Sensor Device"o orixinal"Sistema de desarrollo de sinestesia color-sonido para invidentes utilizando un protocolo de audio""Enseñanza táctil - geometría y color. Juegos didácticos para niños ciegos y videntes""Sistema Constanz"L'ocupació laboral dels cecs a l'Estat espanyol està pràcticament equiparada a la de les persones amb visió, entrevista amb Pedro ZuritaONCE (Organización Nacional de Cegos de España)Prevención da cegueiraDescrición de deficiencias visuais (Disc@pnet)Braillín, un boneco atractivo para calquera neno, con ou sen discapacidade, que permite familiarizarse co sistema de escritura e lectura brailleAxudas Técnicas36838ID00897494007150-90057129528256DOID:1432HP:0000618D001766C10.597.751.941.162C97109C0155020