1

Is there anyone who can help explain why UseString in my following example cannot accept an lvalue as a parameter? I know a temporary String is created in my case, but I cannot explain why it has to accept an rvalue here.

#include <utility>

using namespace std;

struct String
{
    String(const char* cstr)
    {
    }
};

struct UseString
{
    UseString(const String& str)
    {
    }
};

int main()
{
    const char* cstr = "abc";
    UseString(std::move(cstr)); //Correct
    UseString("abc"); // Correct
    UseString(cstr); // Error but why UseString cannot accept lvalue as parameter in this case?

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Tinggo
  • 1,127
  • 1
  • 9
  • 18

1 Answers1

8

The problem is because UseString(cstr); does not do what you think it does.

It is actually a variable declaration, not a constructor call. It is treated exactly the same as UseString cstr; And cstr was already declared earlier, hence the error.

See Which part of the C++ standard allow to declare variable in parenthesis?

Per this Live Demo:

prog.cpp: In function ‘int main()’:
prog.cpp:24:16: error: conflicting declaration ‘UseString cstr’
    UseString(cstr);
                  ^

prog.cpp:21:14: note: previous declaration as ‘const char* cstr’
    const char* cstr = "abc";
                ^~~~

There is no way the compiler can confuse UseString(std::move(cstr)); and UseString("abc"); as variable declarations, so they are treated as calls to the constructor instead.

To solve this, you can use curly braces instead of parenthesis:

UseString{std::move(cstr)};
UseString{"abc"};
UseString{cstr};

Live Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770