Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why C++ strings do not need std::forward to call the desired function?

I'm learning std::forward. I wrote a short program to test what would happen if we do not call std::forward before forwarding the arguments to another function call:

#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;

class Example {
};

ostream &operator << (ostream &os, const Example &e) { os << "yes!"; return os; }

void test_forward_inner(const Example &e) { cout << "& " << e << endl; }
void test_forward_inner(Example &&e) { cout << "&& " << e << endl; }

void test_forward_inner(const string &e) { cout << "& " << e << endl; }
void test_forward_inner(string &&e) { cout << "&& " << e << endl; }

template <typename T>
void test_forward_wrapper(T &&arg) {
    test_forward_inner(arg);
}

int main()
{
    Example e;
    test_forward_wrapper(e);
    test_forward_wrapper(Example());

    cout << endl;

    string s("hello");
    test_forward_wrapper(s);
    test_forward_wrapper("hello");

    return 0;
}

Here I tried to forward a lvalue and an rvalue from test_forward_wrapper() to test_forward_inner(). Running this program gives the output:

& example
& example

& hello
&& hello

For std::strings, the desired inner function was called, but for my own class only the lvalue version was called. Only if I call std::forward before passing the arguments to the inner function can the rvalue version get called.

What makes the difference here? As I know, according to the reference collapsing rules, when the wrapper was called with Example(), an rvalue, T would be deduced as Example and arg would have type Example && thus the rvalue version of the inner function should be called.

And, for other situations like the std::string case here, the correct version of the inner function was called, then can we remove the std::forward here? If not, what (maybe something bad) would happen?

like image 358
pjhades Avatar asked Sep 02 '16 03:09

pjhades


2 Answers

Note that "hello" is not std::string, it's a const char[6]. And test_forward_wrapper() is a function template, the template argument T will be deduced as char const (&)[6] for it.

Inside test_forward_wrapper(), test_forward_inner() is called with const char[6], which need to be converted to std::string at first. This is a temporary std::string, i.e. a rvalue, preferred to be bound to rvalue reference , that's why test_forward_inner(string &&) is called.

Passing an exact std::string to test_forward_wrapper() will get the same result.

test_forward_wrapper(std::string("hello"));
like image 156
songyuanyao Avatar answered Oct 14 '22 06:10

songyuanyao


The difference is that in

test_forward_wrapper("hello");

"hello" here isn't a std::string. It's a const char *.

Change this to a

test_forward_wrapper(std::string("hello"));

And the result will be the same as the custom class's.

like image 43
Sam Varshavchik Avatar answered Oct 14 '22 07:10

Sam Varshavchik