I'm trying to make a class that wraps a pointer around another type. With all of the extraneous bits removed, it looks like this:
template<typename T>
class field {
std::unique_ptr<T> t_;
public:
field() : t_(nullptr) {}
field(const T &t) : t_(std::make_unique<T>(t)) {}
field<T> &operator=(const T &t) {
t_.reset(new T(t));
return *this;
}
};
I can declare them by explicitly calling their constructors, but not with =
, like so:
int main() {
field<std::string> strA("Hello");
field<std::string> strB = "Hello";
return 0;
}
I get the error
-snip-/StringImplicit/main.cpp: In function ‘int main()’:
-snip-/StringImplicit/main.cpp:21:31: error: conversion from ‘const char [6]’ to non-scalar type ‘field<std::__cxx11::basic_string<char> >’ requested
21 | field<std::string> strB = "Hello";
| ^~~~~~~
Where am I going wrong? I can't seem to use field<std::string>
s in class constructors with raw strings without this conversion either, it throws the same error.
Edit: The end goal is something like discordpp::ApplicationCommandOption option{.type = 3, .name = "message", .description = "The message to echo", .required = true};
where all of those parameters are differently-typed fields.
CodePudding user response:
The problem is that two class-type conversions are required:
const char[6]
tostd::string
std::string
tofield<std::string>
.
There is a rule that implicit conversion can have at most one class-type conversion (the official term is "user-defined" conversion although this includes class types that are part of the standard library).
To fix it you can either use your suggested fix; or manually specify one of the conversions, e.g:
auto strB = field<std::string>("Hello");
field<std::string> strC = std::string("Hello");
field<std::string> strD = "Hello"s;
or you could add a constructor for const char[]
.
SFINAE version of char array constructor (there will be better ways in C 20 I'm sure):
template<size_t N, typename = std::enable_if_t<std::is_same_v<T, std::string>>>
field(char const (&t)[N])
: t_(std::make_unique<T>(t)) {}