Why use std::forward instead of static_cast











up vote
17
down vote

favorite
3












When given code of the following structure



template <typename... Args>
void foo(Args&&... args) { ... }


I've often seen library code use static_cast<Args&&> within the function for argument forwarding. Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Given the language's reference collapsing and template deduction rules. We get perfect forwarding with the static_cast<Args&&>, the proof for this claim is below (within error margins, which I am hoping an answer will enlighten)




  • When given rvalue references (or for completeness - no reference qualification as in this example), this collapses the references in such a way that the result is an rvalue. The rule used is && && -> && (rule 1 above)

  • When given lvalue references, this collapses the references in such a way that the result is an lvalue. The rule used here is & && -> & (rule 2 above)


This is essentially getting foo() to forward the arguments to bar() in the example above. This is the behavior you would get when using std::forward<Args> here as well.





Question - why use std::forward in these contexts at all? Does avoiding the extra instantiation justify breaking convention?



Howard Hinnant's paper n2951 specified 6 constraints under which any implementation of std::forward should behave "correctly". These were




  1. Should forward an lvalue as an lvalue

  2. Should forward an rvalue as an rvalue

  3. Should not forward an rvalue as an lvalue

  4. Should forward less cv-qualified expressions to more cv-qualified expressions

  5. Should forward expressions of derived type to an accessible, unambiguous base type

  6. Should not forward arbitrary type conversions


(1) and (2) were proven to work correctly with static_cast<Args&&> above. (3) - (6) don't apply here because when functions are called in a deduced context, none of these can occur.





Note: I personally prefer to use std::forward, but the justification I have is purely that I prefer to stick to convention.










share|improve this question




















  • 1




    I would think in some template library, people avoid using std::forward because they want the library be self-contained, and don't want to use standard library. They will also implement their own std::move, etc.
    – liliscent
    15 hours ago






  • 1




    @VTT Not in this case, because lvalues are typically deduced as T& - wandbox.org/permlink/hPucHiFB2pwh53Ox
    – Curious
    15 hours ago






  • 8




    std::forward and std::move are for readability, you could use static_cast instead to get the same behaviour
    – M.M
    15 hours ago










  • What M.M said. In this answer, Howard mentions std::move was introduced to make the proposal more palatable. Then it stuck. I would imagine the same goes for std::forward.
    – StoryTeller
    14 hours ago












  • @M.M Your point about readability applies. However, std::move is slightly different in my mind because I don’t see the implementation going out of it’s way to prevent against user errors, making it a simpler 1-1 mapping of a cast. The fact that std::forward typically has two overloads, presumably for safety, makes me think that this situation is potentially more complex. Although, I don’t have a good reason to justify that. Hence, the question I guess.
    – Curious
    14 hours ago

















up vote
17
down vote

favorite
3












When given code of the following structure



template <typename... Args>
void foo(Args&&... args) { ... }


I've often seen library code use static_cast<Args&&> within the function for argument forwarding. Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Given the language's reference collapsing and template deduction rules. We get perfect forwarding with the static_cast<Args&&>, the proof for this claim is below (within error margins, which I am hoping an answer will enlighten)




  • When given rvalue references (or for completeness - no reference qualification as in this example), this collapses the references in such a way that the result is an rvalue. The rule used is && && -> && (rule 1 above)

  • When given lvalue references, this collapses the references in such a way that the result is an lvalue. The rule used here is & && -> & (rule 2 above)


This is essentially getting foo() to forward the arguments to bar() in the example above. This is the behavior you would get when using std::forward<Args> here as well.





Question - why use std::forward in these contexts at all? Does avoiding the extra instantiation justify breaking convention?



Howard Hinnant's paper n2951 specified 6 constraints under which any implementation of std::forward should behave "correctly". These were




  1. Should forward an lvalue as an lvalue

  2. Should forward an rvalue as an rvalue

  3. Should not forward an rvalue as an lvalue

  4. Should forward less cv-qualified expressions to more cv-qualified expressions

  5. Should forward expressions of derived type to an accessible, unambiguous base type

  6. Should not forward arbitrary type conversions


(1) and (2) were proven to work correctly with static_cast<Args&&> above. (3) - (6) don't apply here because when functions are called in a deduced context, none of these can occur.





Note: I personally prefer to use std::forward, but the justification I have is purely that I prefer to stick to convention.










share|improve this question




















  • 1




    I would think in some template library, people avoid using std::forward because they want the library be self-contained, and don't want to use standard library. They will also implement their own std::move, etc.
    – liliscent
    15 hours ago






  • 1




    @VTT Not in this case, because lvalues are typically deduced as T& - wandbox.org/permlink/hPucHiFB2pwh53Ox
    – Curious
    15 hours ago






  • 8




    std::forward and std::move are for readability, you could use static_cast instead to get the same behaviour
    – M.M
    15 hours ago










  • What M.M said. In this answer, Howard mentions std::move was introduced to make the proposal more palatable. Then it stuck. I would imagine the same goes for std::forward.
    – StoryTeller
    14 hours ago












  • @M.M Your point about readability applies. However, std::move is slightly different in my mind because I don’t see the implementation going out of it’s way to prevent against user errors, making it a simpler 1-1 mapping of a cast. The fact that std::forward typically has two overloads, presumably for safety, makes me think that this situation is potentially more complex. Although, I don’t have a good reason to justify that. Hence, the question I guess.
    – Curious
    14 hours ago















up vote
17
down vote

favorite
3









up vote
17
down vote

favorite
3






3





When given code of the following structure



template <typename... Args>
void foo(Args&&... args) { ... }


I've often seen library code use static_cast<Args&&> within the function for argument forwarding. Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Given the language's reference collapsing and template deduction rules. We get perfect forwarding with the static_cast<Args&&>, the proof for this claim is below (within error margins, which I am hoping an answer will enlighten)




  • When given rvalue references (or for completeness - no reference qualification as in this example), this collapses the references in such a way that the result is an rvalue. The rule used is && && -> && (rule 1 above)

  • When given lvalue references, this collapses the references in such a way that the result is an lvalue. The rule used here is & && -> & (rule 2 above)


This is essentially getting foo() to forward the arguments to bar() in the example above. This is the behavior you would get when using std::forward<Args> here as well.





Question - why use std::forward in these contexts at all? Does avoiding the extra instantiation justify breaking convention?



Howard Hinnant's paper n2951 specified 6 constraints under which any implementation of std::forward should behave "correctly". These were




  1. Should forward an lvalue as an lvalue

  2. Should forward an rvalue as an rvalue

  3. Should not forward an rvalue as an lvalue

  4. Should forward less cv-qualified expressions to more cv-qualified expressions

  5. Should forward expressions of derived type to an accessible, unambiguous base type

  6. Should not forward arbitrary type conversions


(1) and (2) were proven to work correctly with static_cast<Args&&> above. (3) - (6) don't apply here because when functions are called in a deduced context, none of these can occur.





Note: I personally prefer to use std::forward, but the justification I have is purely that I prefer to stick to convention.










share|improve this question















When given code of the following structure



template <typename... Args>
void foo(Args&&... args) { ... }


I've often seen library code use static_cast<Args&&> within the function for argument forwarding. Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Given the language's reference collapsing and template deduction rules. We get perfect forwarding with the static_cast<Args&&>, the proof for this claim is below (within error margins, which I am hoping an answer will enlighten)




  • When given rvalue references (or for completeness - no reference qualification as in this example), this collapses the references in such a way that the result is an rvalue. The rule used is && && -> && (rule 1 above)

  • When given lvalue references, this collapses the references in such a way that the result is an lvalue. The rule used here is & && -> & (rule 2 above)


This is essentially getting foo() to forward the arguments to bar() in the example above. This is the behavior you would get when using std::forward<Args> here as well.





Question - why use std::forward in these contexts at all? Does avoiding the extra instantiation justify breaking convention?



Howard Hinnant's paper n2951 specified 6 constraints under which any implementation of std::forward should behave "correctly". These were




  1. Should forward an lvalue as an lvalue

  2. Should forward an rvalue as an rvalue

  3. Should not forward an rvalue as an lvalue

  4. Should forward less cv-qualified expressions to more cv-qualified expressions

  5. Should forward expressions of derived type to an accessible, unambiguous base type

  6. Should not forward arbitrary type conversions


(1) and (2) were proven to work correctly with static_cast<Args&&> above. (3) - (6) don't apply here because when functions are called in a deduced context, none of these can occur.





Note: I personally prefer to use std::forward, but the justification I have is purely that I prefer to stick to convention.







c++ templates c++17 rvalue-reference forwarding






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 15 hours ago

























asked 15 hours ago









Curious

11.7k22468




11.7k22468








  • 1




    I would think in some template library, people avoid using std::forward because they want the library be self-contained, and don't want to use standard library. They will also implement their own std::move, etc.
    – liliscent
    15 hours ago






  • 1




    @VTT Not in this case, because lvalues are typically deduced as T& - wandbox.org/permlink/hPucHiFB2pwh53Ox
    – Curious
    15 hours ago






  • 8




    std::forward and std::move are for readability, you could use static_cast instead to get the same behaviour
    – M.M
    15 hours ago










  • What M.M said. In this answer, Howard mentions std::move was introduced to make the proposal more palatable. Then it stuck. I would imagine the same goes for std::forward.
    – StoryTeller
    14 hours ago












  • @M.M Your point about readability applies. However, std::move is slightly different in my mind because I don’t see the implementation going out of it’s way to prevent against user errors, making it a simpler 1-1 mapping of a cast. The fact that std::forward typically has two overloads, presumably for safety, makes me think that this situation is potentially more complex. Although, I don’t have a good reason to justify that. Hence, the question I guess.
    – Curious
    14 hours ago
















  • 1




    I would think in some template library, people avoid using std::forward because they want the library be self-contained, and don't want to use standard library. They will also implement their own std::move, etc.
    – liliscent
    15 hours ago






  • 1




    @VTT Not in this case, because lvalues are typically deduced as T& - wandbox.org/permlink/hPucHiFB2pwh53Ox
    – Curious
    15 hours ago






  • 8




    std::forward and std::move are for readability, you could use static_cast instead to get the same behaviour
    – M.M
    15 hours ago










  • What M.M said. In this answer, Howard mentions std::move was introduced to make the proposal more palatable. Then it stuck. I would imagine the same goes for std::forward.
    – StoryTeller
    14 hours ago












  • @M.M Your point about readability applies. However, std::move is slightly different in my mind because I don’t see the implementation going out of it’s way to prevent against user errors, making it a simpler 1-1 mapping of a cast. The fact that std::forward typically has two overloads, presumably for safety, makes me think that this situation is potentially more complex. Although, I don’t have a good reason to justify that. Hence, the question I guess.
    – Curious
    14 hours ago










1




1




I would think in some template library, people avoid using std::forward because they want the library be self-contained, and don't want to use standard library. They will also implement their own std::move, etc.
– liliscent
15 hours ago




I would think in some template library, people avoid using std::forward because they want the library be self-contained, and don't want to use standard library. They will also implement their own std::move, etc.
– liliscent
15 hours ago




1




1




@VTT Not in this case, because lvalues are typically deduced as T& - wandbox.org/permlink/hPucHiFB2pwh53Ox
– Curious
15 hours ago




@VTT Not in this case, because lvalues are typically deduced as T& - wandbox.org/permlink/hPucHiFB2pwh53Ox
– Curious
15 hours ago




8




8




std::forward and std::move are for readability, you could use static_cast instead to get the same behaviour
– M.M
15 hours ago




std::forward and std::move are for readability, you could use static_cast instead to get the same behaviour
– M.M
15 hours ago












What M.M said. In this answer, Howard mentions std::move was introduced to make the proposal more palatable. Then it stuck. I would imagine the same goes for std::forward.
– StoryTeller
14 hours ago






What M.M said. In this answer, Howard mentions std::move was introduced to make the proposal more palatable. Then it stuck. I would imagine the same goes for std::forward.
– StoryTeller
14 hours ago














@M.M Your point about readability applies. However, std::move is slightly different in my mind because I don’t see the implementation going out of it’s way to prevent against user errors, making it a simpler 1-1 mapping of a cast. The fact that std::forward typically has two overloads, presumably for safety, makes me think that this situation is potentially more complex. Although, I don’t have a good reason to justify that. Hence, the question I guess.
– Curious
14 hours ago






@M.M Your point about readability applies. However, std::move is slightly different in my mind because I don’t see the implementation going out of it’s way to prevent against user errors, making it a simpler 1-1 mapping of a cast. The fact that std::forward typically has two overloads, presumably for safety, makes me think that this situation is potentially more complex. Although, I don’t have a good reason to justify that. Hence, the question I guess.
– Curious
14 hours ago














3 Answers
3






active

oldest

votes

















up vote
11
down vote













Scott Meyers says that std::forward and std::move are mainly for convenience. He even states that std::forward can be used to perform the functionality of both std::forward and std::move.

Some excerpts from "Effective Modern C++":




Item 23:Understand std::move and std::forward

...

The story for std::forward is similar to that for std::move, but whereas std::move unconditionally casts its argument to an rvalue, std::forward does it only under certain conditions. std::forward is a conditional cast. It casts to an rvalue only if its argument was initialized with an rvalue.

...

Given that both std::move and std::forward boil down to casts, the only difference being that std::move always casts, while std::forward only sometimes does, you might ask whether we can dispense with std::move and just use std::forward everywhere. From a purely technical perspective, the answer is yes: std::forward can do it all. std::move isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be, well, yucky.

...
std::move’s attractions are convenience, reduced likelihood of error, and greater clarity...




For those interested, comparison of std::forward<T> vs static_cast<T&&> in assembly (without any optimization) when called with lvalue and rvalue.






share|improve this answer























  • If I want to change an lvalue to an rvalue, I have to use std::move, but not std::forward. Right?
    – Yongwei Wu
    13 hours ago










  • Yes. move casts its argument to an rvalue.
    – P.W
    13 hours ago






  • 2




    Sort of expected this comment from you. :-) I don't think that scott meyers meant that we can just replace move with forward. He gives an example in that item implementing move in terms of forward. " std::move requires only a function argument (rhs.s), while std::forward requires both a function argument (rhs.s) and a template type argument (std::string). Then note that the type we pass to std::forward should be a non-reference, because that’s the convention for encoding that the argument being passed is an rvalue (see Item 28). contd...
    – P.W
    12 hours ago






  • 3




    ...Together, this means that std::move requires less typing than std::forward, and it spares us the trouble of passing a type argument that encodes that the argument we’re passing is an rvalue. It also eliminates the possibility of our passing an incorrect type (e.g., std::string&, which would result in the data member s being copy constructed instead of move constructed). More importantly, the use of std::move conveys an unconditional cast to an rvalue, while the use of std::forward indicates a cast to an rvalue only for references to which rvalues have been bound.
    – P.W
    12 hours ago






  • 1




    @M.M: Yes, looks like Scott Meyers did not take that into account. I was quoting him verbtim . :)
    – P.W
    11 hours ago


















up vote
5
down vote













Here are my 2.5, not so technical cents for this: the fact that today std::forward is indeed just a plain old static_cast<T&&> does not mean that tomorrow it also will be implemented in exactly the same way. I think the committee needed something to reflect the desired behaviour of what std::forward achieves today hence, the forward which does not forward anything anywhere came into existence.



With having the required behaviour and expectations formalized under the umbrella of std::forward, just theoretically speaking, noone impedes a future implementer to provide the std::forward as not the static_cast<T&&> but something specific his own implementation, without actually taking into consideration static_cast<T&&> because the only fact that matters is the correct usage and behaviour of std::forward.






share|improve this answer





















  • I sincerely doubt that forward will ever do something else. Think of how much code that will break...
    – Rakete1111
    11 hours ago






  • 1




    @Rakete1111 That is why my answer is just hypothetical, highly debatable and pure virtual :)
    – fritzone
    11 hours ago


















up vote
4
down vote













forward expresses the intent and it may be safer to use than static_cast: static_cast considers conversion but some dangerous and supposedly non-intentional conversions are detected with forward:



struct A{
A(int);
};

template<class Arg1,class Arg2>
Arg1&& f(Arg1&& a1,Arg2&& a2){
return static_cast<Arg1&&>(a2); // typing error: a1=>a2
}

template<class Arg1,class Arg2>
Arg1&& g(Arg1&& a1,Arg2&& a2){
return forward<Arg1>(a2); // typing error: a1=>a2
}

void test(const A a,int i){
const A& x = f(a,i);//dangling reference
const A& y = g(a,i);//compilation error
}


Example of error message: compiler explorer link





How applies this justification: Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Is the compilation time more problematic than code maintainability? Should the coder even lose its time considering minimizing "unnecessary template instantiation" at every line in the code?



When a template is instantiated, its instantiation causes instantiations of template that are used in its definition and declaration. So that, for example if you have a function as:



  template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}


where foo_1,foo_2,foo_3 are templates, the instantiation of foo will cause 3 instantiations. Then recursively if those functions cause the instantiation of other 3 template functions, you could get 3*3=9 instantiations for example. So you can consider this chain of instantiation as a tree where a root function instantiation can cause thousands of instantiations as an exponentially growing ripple effect. On the other hand a function like forward is a leaf in this instantiation tree. So avoiding its instantiation may only avoid 1 instantiation.



So, the best way to avoid template instantiation explosion is to use dynamic polymorphism for "root" classes and type of argument of "root" functions and then use static polymorphism only for time critical functions that are virtually upper in this instantiation tree.



So, in my opinion using static_cast in place forward to avoid instantiations is a lost of time compared to the benefit of using a more expressive (and safer) code. Template instantiation explosion is more efficiently managed at code architecture level.






share|improve this answer























  • It's not the conversion that is being checked, the check is merely against rvalue -> lvalue forwarding. The code above will compile if g(a, i) is changed to g(std::move(a), i).
    – liliscent
    11 hours ago








  • 2




    How is this brace-style called?
    – YSC
    10 hours ago






  • 1




    @YSC: seems en.wikipedia.org/wiki/Indentation_style#Ratliff_style
    – geza
    8 hours ago











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53257824%2fwhy-use-stdforwardt-instead-of-static-castt%23new-answer', 'question_page');
}
);

Post as a guest
































3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
11
down vote













Scott Meyers says that std::forward and std::move are mainly for convenience. He even states that std::forward can be used to perform the functionality of both std::forward and std::move.

Some excerpts from "Effective Modern C++":




Item 23:Understand std::move and std::forward

...

The story for std::forward is similar to that for std::move, but whereas std::move unconditionally casts its argument to an rvalue, std::forward does it only under certain conditions. std::forward is a conditional cast. It casts to an rvalue only if its argument was initialized with an rvalue.

...

Given that both std::move and std::forward boil down to casts, the only difference being that std::move always casts, while std::forward only sometimes does, you might ask whether we can dispense with std::move and just use std::forward everywhere. From a purely technical perspective, the answer is yes: std::forward can do it all. std::move isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be, well, yucky.

...
std::move’s attractions are convenience, reduced likelihood of error, and greater clarity...




For those interested, comparison of std::forward<T> vs static_cast<T&&> in assembly (without any optimization) when called with lvalue and rvalue.






share|improve this answer























  • If I want to change an lvalue to an rvalue, I have to use std::move, but not std::forward. Right?
    – Yongwei Wu
    13 hours ago










  • Yes. move casts its argument to an rvalue.
    – P.W
    13 hours ago






  • 2




    Sort of expected this comment from you. :-) I don't think that scott meyers meant that we can just replace move with forward. He gives an example in that item implementing move in terms of forward. " std::move requires only a function argument (rhs.s), while std::forward requires both a function argument (rhs.s) and a template type argument (std::string). Then note that the type we pass to std::forward should be a non-reference, because that’s the convention for encoding that the argument being passed is an rvalue (see Item 28). contd...
    – P.W
    12 hours ago






  • 3




    ...Together, this means that std::move requires less typing than std::forward, and it spares us the trouble of passing a type argument that encodes that the argument we’re passing is an rvalue. It also eliminates the possibility of our passing an incorrect type (e.g., std::string&, which would result in the data member s being copy constructed instead of move constructed). More importantly, the use of std::move conveys an unconditional cast to an rvalue, while the use of std::forward indicates a cast to an rvalue only for references to which rvalues have been bound.
    – P.W
    12 hours ago






  • 1




    @M.M: Yes, looks like Scott Meyers did not take that into account. I was quoting him verbtim . :)
    – P.W
    11 hours ago















up vote
11
down vote













Scott Meyers says that std::forward and std::move are mainly for convenience. He even states that std::forward can be used to perform the functionality of both std::forward and std::move.

Some excerpts from "Effective Modern C++":




Item 23:Understand std::move and std::forward

...

The story for std::forward is similar to that for std::move, but whereas std::move unconditionally casts its argument to an rvalue, std::forward does it only under certain conditions. std::forward is a conditional cast. It casts to an rvalue only if its argument was initialized with an rvalue.

...

Given that both std::move and std::forward boil down to casts, the only difference being that std::move always casts, while std::forward only sometimes does, you might ask whether we can dispense with std::move and just use std::forward everywhere. From a purely technical perspective, the answer is yes: std::forward can do it all. std::move isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be, well, yucky.

...
std::move’s attractions are convenience, reduced likelihood of error, and greater clarity...




For those interested, comparison of std::forward<T> vs static_cast<T&&> in assembly (without any optimization) when called with lvalue and rvalue.






share|improve this answer























  • If I want to change an lvalue to an rvalue, I have to use std::move, but not std::forward. Right?
    – Yongwei Wu
    13 hours ago










  • Yes. move casts its argument to an rvalue.
    – P.W
    13 hours ago






  • 2




    Sort of expected this comment from you. :-) I don't think that scott meyers meant that we can just replace move with forward. He gives an example in that item implementing move in terms of forward. " std::move requires only a function argument (rhs.s), while std::forward requires both a function argument (rhs.s) and a template type argument (std::string). Then note that the type we pass to std::forward should be a non-reference, because that’s the convention for encoding that the argument being passed is an rvalue (see Item 28). contd...
    – P.W
    12 hours ago






  • 3




    ...Together, this means that std::move requires less typing than std::forward, and it spares us the trouble of passing a type argument that encodes that the argument we’re passing is an rvalue. It also eliminates the possibility of our passing an incorrect type (e.g., std::string&, which would result in the data member s being copy constructed instead of move constructed). More importantly, the use of std::move conveys an unconditional cast to an rvalue, while the use of std::forward indicates a cast to an rvalue only for references to which rvalues have been bound.
    – P.W
    12 hours ago






  • 1




    @M.M: Yes, looks like Scott Meyers did not take that into account. I was quoting him verbtim . :)
    – P.W
    11 hours ago













up vote
11
down vote










up vote
11
down vote









Scott Meyers says that std::forward and std::move are mainly for convenience. He even states that std::forward can be used to perform the functionality of both std::forward and std::move.

Some excerpts from "Effective Modern C++":




Item 23:Understand std::move and std::forward

...

The story for std::forward is similar to that for std::move, but whereas std::move unconditionally casts its argument to an rvalue, std::forward does it only under certain conditions. std::forward is a conditional cast. It casts to an rvalue only if its argument was initialized with an rvalue.

...

Given that both std::move and std::forward boil down to casts, the only difference being that std::move always casts, while std::forward only sometimes does, you might ask whether we can dispense with std::move and just use std::forward everywhere. From a purely technical perspective, the answer is yes: std::forward can do it all. std::move isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be, well, yucky.

...
std::move’s attractions are convenience, reduced likelihood of error, and greater clarity...




For those interested, comparison of std::forward<T> vs static_cast<T&&> in assembly (without any optimization) when called with lvalue and rvalue.






share|improve this answer














Scott Meyers says that std::forward and std::move are mainly for convenience. He even states that std::forward can be used to perform the functionality of both std::forward and std::move.

Some excerpts from "Effective Modern C++":




Item 23:Understand std::move and std::forward

...

The story for std::forward is similar to that for std::move, but whereas std::move unconditionally casts its argument to an rvalue, std::forward does it only under certain conditions. std::forward is a conditional cast. It casts to an rvalue only if its argument was initialized with an rvalue.

...

Given that both std::move and std::forward boil down to casts, the only difference being that std::move always casts, while std::forward only sometimes does, you might ask whether we can dispense with std::move and just use std::forward everywhere. From a purely technical perspective, the answer is yes: std::forward can do it all. std::move isn’t necessary. Of course, neither function is really necessary, because we could write casts everywhere, but I hope we agree that that would be, well, yucky.

...
std::move’s attractions are convenience, reduced likelihood of error, and greater clarity...




For those interested, comparison of std::forward<T> vs static_cast<T&&> in assembly (without any optimization) when called with lvalue and rvalue.







share|improve this answer














share|improve this answer



share|improve this answer








edited 11 hours ago

























answered 13 hours ago









P.W

7,0241438




7,0241438












  • If I want to change an lvalue to an rvalue, I have to use std::move, but not std::forward. Right?
    – Yongwei Wu
    13 hours ago










  • Yes. move casts its argument to an rvalue.
    – P.W
    13 hours ago






  • 2




    Sort of expected this comment from you. :-) I don't think that scott meyers meant that we can just replace move with forward. He gives an example in that item implementing move in terms of forward. " std::move requires only a function argument (rhs.s), while std::forward requires both a function argument (rhs.s) and a template type argument (std::string). Then note that the type we pass to std::forward should be a non-reference, because that’s the convention for encoding that the argument being passed is an rvalue (see Item 28). contd...
    – P.W
    12 hours ago






  • 3




    ...Together, this means that std::move requires less typing than std::forward, and it spares us the trouble of passing a type argument that encodes that the argument we’re passing is an rvalue. It also eliminates the possibility of our passing an incorrect type (e.g., std::string&, which would result in the data member s being copy constructed instead of move constructed). More importantly, the use of std::move conveys an unconditional cast to an rvalue, while the use of std::forward indicates a cast to an rvalue only for references to which rvalues have been bound.
    – P.W
    12 hours ago






  • 1




    @M.M: Yes, looks like Scott Meyers did not take that into account. I was quoting him verbtim . :)
    – P.W
    11 hours ago


















  • If I want to change an lvalue to an rvalue, I have to use std::move, but not std::forward. Right?
    – Yongwei Wu
    13 hours ago










  • Yes. move casts its argument to an rvalue.
    – P.W
    13 hours ago






  • 2




    Sort of expected this comment from you. :-) I don't think that scott meyers meant that we can just replace move with forward. He gives an example in that item implementing move in terms of forward. " std::move requires only a function argument (rhs.s), while std::forward requires both a function argument (rhs.s) and a template type argument (std::string). Then note that the type we pass to std::forward should be a non-reference, because that’s the convention for encoding that the argument being passed is an rvalue (see Item 28). contd...
    – P.W
    12 hours ago






  • 3




    ...Together, this means that std::move requires less typing than std::forward, and it spares us the trouble of passing a type argument that encodes that the argument we’re passing is an rvalue. It also eliminates the possibility of our passing an incorrect type (e.g., std::string&, which would result in the data member s being copy constructed instead of move constructed). More importantly, the use of std::move conveys an unconditional cast to an rvalue, while the use of std::forward indicates a cast to an rvalue only for references to which rvalues have been bound.
    – P.W
    12 hours ago






  • 1




    @M.M: Yes, looks like Scott Meyers did not take that into account. I was quoting him verbtim . :)
    – P.W
    11 hours ago
















If I want to change an lvalue to an rvalue, I have to use std::move, but not std::forward. Right?
– Yongwei Wu
13 hours ago




If I want to change an lvalue to an rvalue, I have to use std::move, but not std::forward. Right?
– Yongwei Wu
13 hours ago












Yes. move casts its argument to an rvalue.
– P.W
13 hours ago




Yes. move casts its argument to an rvalue.
– P.W
13 hours ago




2




2




Sort of expected this comment from you. :-) I don't think that scott meyers meant that we can just replace move with forward. He gives an example in that item implementing move in terms of forward. " std::move requires only a function argument (rhs.s), while std::forward requires both a function argument (rhs.s) and a template type argument (std::string). Then note that the type we pass to std::forward should be a non-reference, because that’s the convention for encoding that the argument being passed is an rvalue (see Item 28). contd...
– P.W
12 hours ago




Sort of expected this comment from you. :-) I don't think that scott meyers meant that we can just replace move with forward. He gives an example in that item implementing move in terms of forward. " std::move requires only a function argument (rhs.s), while std::forward requires both a function argument (rhs.s) and a template type argument (std::string). Then note that the type we pass to std::forward should be a non-reference, because that’s the convention for encoding that the argument being passed is an rvalue (see Item 28). contd...
– P.W
12 hours ago




3




3




...Together, this means that std::move requires less typing than std::forward, and it spares us the trouble of passing a type argument that encodes that the argument we’re passing is an rvalue. It also eliminates the possibility of our passing an incorrect type (e.g., std::string&, which would result in the data member s being copy constructed instead of move constructed). More importantly, the use of std::move conveys an unconditional cast to an rvalue, while the use of std::forward indicates a cast to an rvalue only for references to which rvalues have been bound.
– P.W
12 hours ago




...Together, this means that std::move requires less typing than std::forward, and it spares us the trouble of passing a type argument that encodes that the argument we’re passing is an rvalue. It also eliminates the possibility of our passing an incorrect type (e.g., std::string&, which would result in the data member s being copy constructed instead of move constructed). More importantly, the use of std::move conveys an unconditional cast to an rvalue, while the use of std::forward indicates a cast to an rvalue only for references to which rvalues have been bound.
– P.W
12 hours ago




1




1




@M.M: Yes, looks like Scott Meyers did not take that into account. I was quoting him verbtim . :)
– P.W
11 hours ago




@M.M: Yes, looks like Scott Meyers did not take that into account. I was quoting him verbtim . :)
– P.W
11 hours ago












up vote
5
down vote













Here are my 2.5, not so technical cents for this: the fact that today std::forward is indeed just a plain old static_cast<T&&> does not mean that tomorrow it also will be implemented in exactly the same way. I think the committee needed something to reflect the desired behaviour of what std::forward achieves today hence, the forward which does not forward anything anywhere came into existence.



With having the required behaviour and expectations formalized under the umbrella of std::forward, just theoretically speaking, noone impedes a future implementer to provide the std::forward as not the static_cast<T&&> but something specific his own implementation, without actually taking into consideration static_cast<T&&> because the only fact that matters is the correct usage and behaviour of std::forward.






share|improve this answer





















  • I sincerely doubt that forward will ever do something else. Think of how much code that will break...
    – Rakete1111
    11 hours ago






  • 1




    @Rakete1111 That is why my answer is just hypothetical, highly debatable and pure virtual :)
    – fritzone
    11 hours ago















up vote
5
down vote













Here are my 2.5, not so technical cents for this: the fact that today std::forward is indeed just a plain old static_cast<T&&> does not mean that tomorrow it also will be implemented in exactly the same way. I think the committee needed something to reflect the desired behaviour of what std::forward achieves today hence, the forward which does not forward anything anywhere came into existence.



With having the required behaviour and expectations formalized under the umbrella of std::forward, just theoretically speaking, noone impedes a future implementer to provide the std::forward as not the static_cast<T&&> but something specific his own implementation, without actually taking into consideration static_cast<T&&> because the only fact that matters is the correct usage and behaviour of std::forward.






share|improve this answer





















  • I sincerely doubt that forward will ever do something else. Think of how much code that will break...
    – Rakete1111
    11 hours ago






  • 1




    @Rakete1111 That is why my answer is just hypothetical, highly debatable and pure virtual :)
    – fritzone
    11 hours ago













up vote
5
down vote










up vote
5
down vote









Here are my 2.5, not so technical cents for this: the fact that today std::forward is indeed just a plain old static_cast<T&&> does not mean that tomorrow it also will be implemented in exactly the same way. I think the committee needed something to reflect the desired behaviour of what std::forward achieves today hence, the forward which does not forward anything anywhere came into existence.



With having the required behaviour and expectations formalized under the umbrella of std::forward, just theoretically speaking, noone impedes a future implementer to provide the std::forward as not the static_cast<T&&> but something specific his own implementation, without actually taking into consideration static_cast<T&&> because the only fact that matters is the correct usage and behaviour of std::forward.






share|improve this answer












Here are my 2.5, not so technical cents for this: the fact that today std::forward is indeed just a plain old static_cast<T&&> does not mean that tomorrow it also will be implemented in exactly the same way. I think the committee needed something to reflect the desired behaviour of what std::forward achieves today hence, the forward which does not forward anything anywhere came into existence.



With having the required behaviour and expectations formalized under the umbrella of std::forward, just theoretically speaking, noone impedes a future implementer to provide the std::forward as not the static_cast<T&&> but something specific his own implementation, without actually taking into consideration static_cast<T&&> because the only fact that matters is the correct usage and behaviour of std::forward.







share|improve this answer












share|improve this answer



share|improve this answer










answered 15 hours ago









fritzone

19.8k1167116




19.8k1167116












  • I sincerely doubt that forward will ever do something else. Think of how much code that will break...
    – Rakete1111
    11 hours ago






  • 1




    @Rakete1111 That is why my answer is just hypothetical, highly debatable and pure virtual :)
    – fritzone
    11 hours ago


















  • I sincerely doubt that forward will ever do something else. Think of how much code that will break...
    – Rakete1111
    11 hours ago






  • 1




    @Rakete1111 That is why my answer is just hypothetical, highly debatable and pure virtual :)
    – fritzone
    11 hours ago
















I sincerely doubt that forward will ever do something else. Think of how much code that will break...
– Rakete1111
11 hours ago




I sincerely doubt that forward will ever do something else. Think of how much code that will break...
– Rakete1111
11 hours ago




1




1




@Rakete1111 That is why my answer is just hypothetical, highly debatable and pure virtual :)
– fritzone
11 hours ago




@Rakete1111 That is why my answer is just hypothetical, highly debatable and pure virtual :)
– fritzone
11 hours ago










up vote
4
down vote













forward expresses the intent and it may be safer to use than static_cast: static_cast considers conversion but some dangerous and supposedly non-intentional conversions are detected with forward:



struct A{
A(int);
};

template<class Arg1,class Arg2>
Arg1&& f(Arg1&& a1,Arg2&& a2){
return static_cast<Arg1&&>(a2); // typing error: a1=>a2
}

template<class Arg1,class Arg2>
Arg1&& g(Arg1&& a1,Arg2&& a2){
return forward<Arg1>(a2); // typing error: a1=>a2
}

void test(const A a,int i){
const A& x = f(a,i);//dangling reference
const A& y = g(a,i);//compilation error
}


Example of error message: compiler explorer link





How applies this justification: Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Is the compilation time more problematic than code maintainability? Should the coder even lose its time considering minimizing "unnecessary template instantiation" at every line in the code?



When a template is instantiated, its instantiation causes instantiations of template that are used in its definition and declaration. So that, for example if you have a function as:



  template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}


where foo_1,foo_2,foo_3 are templates, the instantiation of foo will cause 3 instantiations. Then recursively if those functions cause the instantiation of other 3 template functions, you could get 3*3=9 instantiations for example. So you can consider this chain of instantiation as a tree where a root function instantiation can cause thousands of instantiations as an exponentially growing ripple effect. On the other hand a function like forward is a leaf in this instantiation tree. So avoiding its instantiation may only avoid 1 instantiation.



So, the best way to avoid template instantiation explosion is to use dynamic polymorphism for "root" classes and type of argument of "root" functions and then use static polymorphism only for time critical functions that are virtually upper in this instantiation tree.



So, in my opinion using static_cast in place forward to avoid instantiations is a lost of time compared to the benefit of using a more expressive (and safer) code. Template instantiation explosion is more efficiently managed at code architecture level.






share|improve this answer























  • It's not the conversion that is being checked, the check is merely against rvalue -> lvalue forwarding. The code above will compile if g(a, i) is changed to g(std::move(a), i).
    – liliscent
    11 hours ago








  • 2




    How is this brace-style called?
    – YSC
    10 hours ago






  • 1




    @YSC: seems en.wikipedia.org/wiki/Indentation_style#Ratliff_style
    – geza
    8 hours ago















up vote
4
down vote













forward expresses the intent and it may be safer to use than static_cast: static_cast considers conversion but some dangerous and supposedly non-intentional conversions are detected with forward:



struct A{
A(int);
};

template<class Arg1,class Arg2>
Arg1&& f(Arg1&& a1,Arg2&& a2){
return static_cast<Arg1&&>(a2); // typing error: a1=>a2
}

template<class Arg1,class Arg2>
Arg1&& g(Arg1&& a1,Arg2&& a2){
return forward<Arg1>(a2); // typing error: a1=>a2
}

void test(const A a,int i){
const A& x = f(a,i);//dangling reference
const A& y = g(a,i);//compilation error
}


Example of error message: compiler explorer link





How applies this justification: Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Is the compilation time more problematic than code maintainability? Should the coder even lose its time considering minimizing "unnecessary template instantiation" at every line in the code?



When a template is instantiated, its instantiation causes instantiations of template that are used in its definition and declaration. So that, for example if you have a function as:



  template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}


where foo_1,foo_2,foo_3 are templates, the instantiation of foo will cause 3 instantiations. Then recursively if those functions cause the instantiation of other 3 template functions, you could get 3*3=9 instantiations for example. So you can consider this chain of instantiation as a tree where a root function instantiation can cause thousands of instantiations as an exponentially growing ripple effect. On the other hand a function like forward is a leaf in this instantiation tree. So avoiding its instantiation may only avoid 1 instantiation.



So, the best way to avoid template instantiation explosion is to use dynamic polymorphism for "root" classes and type of argument of "root" functions and then use static polymorphism only for time critical functions that are virtually upper in this instantiation tree.



So, in my opinion using static_cast in place forward to avoid instantiations is a lost of time compared to the benefit of using a more expressive (and safer) code. Template instantiation explosion is more efficiently managed at code architecture level.






share|improve this answer























  • It's not the conversion that is being checked, the check is merely against rvalue -> lvalue forwarding. The code above will compile if g(a, i) is changed to g(std::move(a), i).
    – liliscent
    11 hours ago








  • 2




    How is this brace-style called?
    – YSC
    10 hours ago






  • 1




    @YSC: seems en.wikipedia.org/wiki/Indentation_style#Ratliff_style
    – geza
    8 hours ago













up vote
4
down vote










up vote
4
down vote









forward expresses the intent and it may be safer to use than static_cast: static_cast considers conversion but some dangerous and supposedly non-intentional conversions are detected with forward:



struct A{
A(int);
};

template<class Arg1,class Arg2>
Arg1&& f(Arg1&& a1,Arg2&& a2){
return static_cast<Arg1&&>(a2); // typing error: a1=>a2
}

template<class Arg1,class Arg2>
Arg1&& g(Arg1&& a1,Arg2&& a2){
return forward<Arg1>(a2); // typing error: a1=>a2
}

void test(const A a,int i){
const A& x = f(a,i);//dangling reference
const A& y = g(a,i);//compilation error
}


Example of error message: compiler explorer link





How applies this justification: Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Is the compilation time more problematic than code maintainability? Should the coder even lose its time considering minimizing "unnecessary template instantiation" at every line in the code?



When a template is instantiated, its instantiation causes instantiations of template that are used in its definition and declaration. So that, for example if you have a function as:



  template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}


where foo_1,foo_2,foo_3 are templates, the instantiation of foo will cause 3 instantiations. Then recursively if those functions cause the instantiation of other 3 template functions, you could get 3*3=9 instantiations for example. So you can consider this chain of instantiation as a tree where a root function instantiation can cause thousands of instantiations as an exponentially growing ripple effect. On the other hand a function like forward is a leaf in this instantiation tree. So avoiding its instantiation may only avoid 1 instantiation.



So, the best way to avoid template instantiation explosion is to use dynamic polymorphism for "root" classes and type of argument of "root" functions and then use static polymorphism only for time critical functions that are virtually upper in this instantiation tree.



So, in my opinion using static_cast in place forward to avoid instantiations is a lost of time compared to the benefit of using a more expressive (and safer) code. Template instantiation explosion is more efficiently managed at code architecture level.






share|improve this answer














forward expresses the intent and it may be safer to use than static_cast: static_cast considers conversion but some dangerous and supposedly non-intentional conversions are detected with forward:



struct A{
A(int);
};

template<class Arg1,class Arg2>
Arg1&& f(Arg1&& a1,Arg2&& a2){
return static_cast<Arg1&&>(a2); // typing error: a1=>a2
}

template<class Arg1,class Arg2>
Arg1&& g(Arg1&& a1,Arg2&& a2){
return forward<Arg1>(a2); // typing error: a1=>a2
}

void test(const A a,int i){
const A& x = f(a,i);//dangling reference
const A& y = g(a,i);//compilation error
}


Example of error message: compiler explorer link





How applies this justification: Typically, the justification for this is that using a static_cast avoids an unnecessary template instantiation.



Is the compilation time more problematic than code maintainability? Should the coder even lose its time considering minimizing "unnecessary template instantiation" at every line in the code?



When a template is instantiated, its instantiation causes instantiations of template that are used in its definition and declaration. So that, for example if you have a function as:



  template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}


where foo_1,foo_2,foo_3 are templates, the instantiation of foo will cause 3 instantiations. Then recursively if those functions cause the instantiation of other 3 template functions, you could get 3*3=9 instantiations for example. So you can consider this chain of instantiation as a tree where a root function instantiation can cause thousands of instantiations as an exponentially growing ripple effect. On the other hand a function like forward is a leaf in this instantiation tree. So avoiding its instantiation may only avoid 1 instantiation.



So, the best way to avoid template instantiation explosion is to use dynamic polymorphism for "root" classes and type of argument of "root" functions and then use static polymorphism only for time critical functions that are virtually upper in this instantiation tree.



So, in my opinion using static_cast in place forward to avoid instantiations is a lost of time compared to the benefit of using a more expressive (and safer) code. Template instantiation explosion is more efficiently managed at code architecture level.







share|improve this answer














share|improve this answer



share|improve this answer








edited 5 hours ago

























answered 12 hours ago









Oliv

7,2701752




7,2701752












  • It's not the conversion that is being checked, the check is merely against rvalue -> lvalue forwarding. The code above will compile if g(a, i) is changed to g(std::move(a), i).
    – liliscent
    11 hours ago








  • 2




    How is this brace-style called?
    – YSC
    10 hours ago






  • 1




    @YSC: seems en.wikipedia.org/wiki/Indentation_style#Ratliff_style
    – geza
    8 hours ago


















  • It's not the conversion that is being checked, the check is merely against rvalue -> lvalue forwarding. The code above will compile if g(a, i) is changed to g(std::move(a), i).
    – liliscent
    11 hours ago








  • 2




    How is this brace-style called?
    – YSC
    10 hours ago






  • 1




    @YSC: seems en.wikipedia.org/wiki/Indentation_style#Ratliff_style
    – geza
    8 hours ago
















It's not the conversion that is being checked, the check is merely against rvalue -> lvalue forwarding. The code above will compile if g(a, i) is changed to g(std::move(a), i).
– liliscent
11 hours ago






It's not the conversion that is being checked, the check is merely against rvalue -> lvalue forwarding. The code above will compile if g(a, i) is changed to g(std::move(a), i).
– liliscent
11 hours ago






2




2




How is this brace-style called?
– YSC
10 hours ago




How is this brace-style called?
– YSC
10 hours ago




1




1




@YSC: seems en.wikipedia.org/wiki/Indentation_style#Ratliff_style
– geza
8 hours ago




@YSC: seems en.wikipedia.org/wiki/Indentation_style#Ratliff_style
– geza
8 hours ago


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53257824%2fwhy-use-stdforwardt-instead-of-static-castt%23new-answer', 'question_page');
}
);

Post as a guest




















































































Popular posts from this blog

Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

ComboBox Display Member on multiple fields

Is it possible to collect Nectar points via Trainline?