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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With