Following is the code brought from this question.
// [I]
void overloaded_function(const std::string& param) {
std::cout << "const std::string& version: " << param << '\n';
}
// [II]
void overloaded_function(std::string&& param) {
std::cout << "std::string&& version: " << param << '\n';
}
template <typename T>
void pass_through_f(T&& param) {
overloaded_function(std::forward<T>(param));
}
template <typename T>
void pass_through(T&& param) {
overloaded_function(param);
}
int main() {
std::string str = "Hello World";
pass_through_f(str); // (1)
pass_through_f(std::move(str)); // (2)
std::cout << "----------\n";
pass_through(str); // (3)
pass_through(std::move(str)); // (4)
return 0;
}
const std::string& version: Hello World
std::string&& version: Hello World
----------
const std::string& version: Hello World
const std::string& version: Hello World
When I use pass_through_f(), the result between (1) and (2) is different but they are same when pass_though() is called.
My question is, how is the result of (4) same with (3)? Here's type inferencing process that I thought:
In (4), return value of some function is r-value, so T of pass_through() is inferred as a type same with passed argument. Thus T is std::string&&.
std::string&& is r-value reference, and r-value reference is r-value so it calls overloaded_function() [II]. Thus its result is same with when std::forward is used.
But result is different with my thought. I think I'm misunderstanding something about the way std::forward works.
The problem is that in
template <typename T>
void pass_through(T&& param) {
overloaded_function(param);
}
param is a named variable. Since it has a name, it is an lvalue, even though it is a reference to an rvalue. Since it is an lvalue, you call the lvalue function.
You need std::forward to "re-cast" it back into a rvalue if it came in as an rvalue.
Also note that T is not string&&. Since you passed a string&&, T&& deduces T to string and the && is kept to make it an rvalue reference.
Related
code as follows:
#include <iostream>
#include<functional>
class TestCase {
public:
template<typename ...Args>
void myfun(Args&& ...args) {
sayhello(std::forward<Args>(args)...);
}
private:
void sayhello(const std::string& v, const std::string& v2) {
std::cout <<" ---1---- " << v << "," << v2 << std::endl;
}
};
template<typename ...Args>
void test(Args&& ...args) {
TestCase a;
auto f = std::bind(&TestCase::myfun<Args...>, &a,std::forward<Args>(args)...);
f();
}
int main() {
std::string a = "1";
std::string b = "2";
test(a,b); //ok
test("1","2"); //error
return 0;
}
why std::forward not work? I tried pass value by lvalue and it work,but pass value by rvalue not work.I confused with TestCase::myfun<Args...> ,should it be TestCase::myfun<Args&&...> or something else?
The problem is similar to that in std::bind and rvalue reference.
std::bind will always copy the arguments and store them. When you use "1", "2", TestCase::myfun<Args...> is instantiated with Args = const char &[2], while the arguments are stored as const char * (due to the copy), so the error occurs since the parameter const char &[2] does not match arguments of const char *.
When you use std::string("1"), std::string("2"), TestCase::myfun<Args...> is instantiated with Args = std::string (so the parameter type is std::string &&). The error still occurs since the parameter std::string && does not match the stored arguments, which are lvalues.
As you have found, using lambda is a proper solution.
I have made simple test to understand better move semantics. Output result was unexpected for me. Bellow is my test function:
template<class T>
void test(T&& v)
{
v++;
}
void main()
{
int v = 1;
test(std::move(v));
std::cout << "Output:" << v << std::endl;
}
My expectation was:
Output: 1
But real result was:
Output: 2
I thought as follows - I use std::move(v), as result I have conversion to "rvalue" and test function will work with temporal variable. Therefore the result should be Output: 1.
What is wrong in my conclusions?
std::move never constructs a temporary. It is a conversion to an rvalue-reference.
This reference is bound to main::v, then test::v parameter is initialized with it, and thus also bound to main::v.
If you want to create a temporary, use a cast:
test(int{v});
To help you understand what type your template deduced, you can use the macro __PRETTY_FUNCTION__
template<class T>
void test(T&& v)
{
std::cout<< __PRETTY_FUNCTION__ << std::endl;
v++;
}
Also this guys explains a lot of things about template deduction (your case at 35')
https://www.youtube.com/watch?v=vwrXHznaYLA
There are 2 function overloads:
MyClass do_something(MyClass param);
const MyClass& do_something(const MyClass& param);
Then I do:
MyClass c1 {"c1"};
do_something(c1); // I want this to be used by value overload
do_something(c1); // this to be used by reference overload
Is there any special way to explicitly specify that argument is passed by value or by reference?
For move semantic there is std::move() I wonder if there is anything like std::copy() std::ref for my case?
P.S. It's not to be used in real program, just checking out by myself the difference of passing arguments, returning values and their behaviour in different ways and have all functions with the same name:
// pass by value (copy)
MyClass do_something(MyClass param) {
cout << "do_something(MyClass param)" << endl;
param.i = 100;
return param;
}
// !!! Your normal habit when passing an argument to a function should be to pass by const reference. (thinking in c++)
// pass by reference (reference)
const MyClass& do_something(const MyClass& param) { // doesn't allow to modify the object
cout << "do_something(MyClass& param)" << endl;
return param;
}
// pass by move semantic (move)
MyClass&& do_something(MyClass&& param) {
cout << "do_something(MyClass&& param)" << endl;
param.name += "__after_do_something(MyClass&& param)";
param.i = 100;
return move(param);
}
// pass by pointer (reference)
MyClass* do_something(MyClass* const param) { // allows to modify object, but not pointer (address)
cout << "do_something(MyClass* const param)" << endl;
param->i = 100;
// (*param).i = 100; // the same as above
return param;
}
You can resolve an overload ambiguity by casting to the relevant function pointer type (it's one of the rare cases where the type of an expression is determined by outer context, instead of being built up from inside):
struct MyClass { char const* s; };
MyClass do_something(MyClass) { return MyClass(); }
const MyClass& do_something(const MyClass& param) { return param; }
auto main() -> int
{
MyClass c1 {"c1"};
static_cast<MyClass(*)(MyClass)>( do_something )( c1 ); // Value overload
static_cast<MyClass const&(*)(MyClass const&)>( do_something )( c1 ); // Ref overload
}
But in practice you should just name the functions differently, or use tie-breaker arguments or argument types, i.e., designing the functions for explicit choice of function.
I would name them differently because they do different things, so it indicates the Wrong Thing™ to have the same name for them.
Is there any special way to explicitly specify that argument is passed by value or by reference?
No, but there are workaround.
With template method and specialization, you may explicitly tell which version you want:
template <typename T>
T do_something(T);
template<>
MyClass do_something(MyClass) { std::cout << "value" << std::endl; }
template<>
const MyClass& do_something(const MyClass&)
{
std::cout << "reference" << std::endl;
}
And then call it:
do_something<MyClass>(c); // value
do_something<const MyClass&>(c); // reference
do_something(c); // value
Live Demo
But it would be simpler/cleaner to create overload with explicit tag:
struct by_value{};
struct by_ref{};
MyClass do_something(MyClass, by_value) { std::cout << "value" << std::endl; }
const MyClass& do_something(const MyClass&, by_ref)
{
std::cout << "reference" << std::endl;
}
And call it
do_something(c, by_value{}); // value
do_something(c, by_ref{}); // reference
Is there any special way to explicitly specify that argument is passed by value or by reference?
Yes. If it is not copyable (i.e., deleted or private copy constructor) it can only be passed by reference.
See the piece of code below(notice that s is a array with char instead of string):
#include <string>
#include <iostream>
#include <utility>
void func(std::string & s, char a) {
std::cout << "here1" << std::endl;
// ...
}
void func(std::string && s, char a) {
std::cout << "here2" << std::endl;
// ...
}
template <class T>
void foo(T && s) {
std::cout << "foo2" << std::endl;
func(s, ':');
//func(std::forward<T>(s), ':');
}
int main(int agrc, char *argv[])
{
//std::string s("a:b:c:d");
char s[8] = "abcdefg";
foo(std::move(s));
std::string s2("abcd")
foo(s2);
return 0;
}
If I replace func(s, ':') using std::forward, it makes no difference, the foo function will do the prefect forwarding without std::forward in this case.
Is there mis-understanding of 'prefect forwarding' with std::forward in C++11?
It makes a difference. In your case func(s, ':') will only ever call the overload of func that takes reference to a std::string.
The issue is that named variables and parameters are lvalues themselves and this is also the reason why std::forward exists in the first place.
In your example it has the appearance that the right overload is chosen for the first call to foo even if you don't use std::forward. This is just because you actually call foo with a char[] argument which is used to implicitly construct a temporary of std::string.
Lets consider first case foo(std::move(s)). Inside of foo, s will be bind to rvalue. But since it has a name (s) it's not an rvalue anymore. So when you pass s to func it will be like passing lvalue to it - it will trigger here1 version. But if you will pass s via std::forward it will be rvalue once again.
In second case s is lvalue reference when foo is entered, so it doesn't change anything whether you pass it directly or via std::forward. You would need to use std::move to get rvalue out of s again.
Is there a way to remove the 'plumb' version of all of my functions, without the need to change the 'hit' line to the 'fixed'?
Yes my program works fine, but I think if is there a way to get ride from this version of all of my functions.
Keep in mind that int is not really int in my programs, but a type alias which can be object ( e.g. container_reference<std::array<double,4>> ) or reference ( e.g. std::array<double,4> & )
void func(int &&m) { cout << "rvalue: " << m << endl; }
void func(int &m) { cout << "lvalue: "; func(std::move(m)); } // PLUMB!
int main()
{
int a = 5;
func(a); // HIT!
func(std::move(a)); // FIXED!
func(6);
func(a + 5);
}
I'm having a bit of trouble understand exactly what you want, but this might be an option:
template<typename T>
void func(T &&m) {
// ...
}
T&& has been dubbed "universal reference" as it will bind to both lvalues and rvalues due to reference collapsing rules.