Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does copy initialization of my class not work with string_view using string literals?

I have the following code:

#include <string_view>

class Foo
{
public:
    Foo(std::string_view) {}
};

When I do this, everything compiles fine (using clang v8, C++17):

Foo f("testing");

However, if I use copy initialization it fails:

Foo f = "testing";

Diagnostic:

prog.cc:15:9: error: no viable conversion from 'const char [8]' to 'Foo'
    Foo f = "testing";
        ^   ~~~~~~~~~
prog.cc:7:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'const char [8]' to 'const Foo &' for 1st argument
class Foo
      ^
prog.cc:7:7: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'const char [8]' to 'Foo &&' for 1st argument
class Foo
      ^
prog.cc:10:5: note: candidate constructor not viable: no known conversion from 'const char [8]' to 'std::string_view' (aka 'basic_string_view<char>') for 1st argument
    Foo(std::string_view) {}
    ^
1 error generated.

Looking at the constructors for string_view, I don't see any overloads that take char const[], is that maybe the issue? I realize I could use "testing"sv to solve this but I feel like the string literal case should work as well.

Why doesn't the copy initialization case work? How can I make it work with string literals?

like image 233
void.pointer Avatar asked Jan 27 '23 01:01

void.pointer


1 Answers

Foo f = "testing"; is copy initialization, which asks for implicit conversion from "testing" (which is of type const char[8]) to Foo. "testing" could decay to const char*, and then still two user-defined conversions are required. The conversion from const char* to std::string_view, and one from std::string_view to Foo. But only one user-defined conversion is allowed in one implicit conversion sequence.

Foo f("testing"); is direct initialization, which behaves differently. "testing" decays to const char* and then converts to std::string_view, which is used as the argument of Foo's constructor to initialize f directly.

In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.

Implicit conversion is defined in terms of copy-initialization: if an object of type T can be copy-initialized with expression E, then E is implicitly convertible to T.

As the workaround, if you want to stick to copy-initialization, you can reduce the required user-defined conversion. As you showed it's a good idea to apply Foo f = "testing"sv;, or Foo f = std::string_view("testing");, which has the same effect.

like image 114
songyuanyao Avatar answered Jan 31 '23 23:01

songyuanyao