我正在尝试创建一个可以隐式转换为各种不同类型的类,包括基元类型和自定义定义的类。我希望能够转换为的类型之一是 std::string。下面是一个可以转换为各种不同类型的示例类。它抛出错误“错误:‘operator=’的重载不明确”。这是因为 std::string 有一个来自 CharT 的赋值运算符,编译器可以从 int 创建该运算符。我的问题是,是否有可能有一个类可以隐式转换为整数或字符串类型?
class Test {
public:
operator double() const {
return 3.141592;
}
operator std::int64_t() const {
return -999;
}
operator std::uint64_t() const {
return 999;
}
operator bool() const {
return true;
}
operator std::string() const {
return "abcd";
}
};
int main(int argc, char** argv) {
std::string test_str = Test();
test_str = Test();
std::cout << test_str;
}
有趣的是,当我在定义它的同一行上分配给 test_str 时,编译器不会抛出任何错误,因为它使用简单的构造函数而不是赋值运算符,但它在下一行出错。
任何帮助将不胜感激。
std::string
有几个重载的 operator=
,其参数如下:std::string
、const char *
、char
、std::initializer_list
。
为了让你的代码正常工作,编译器需要选择一个,但至少有两个可能是合适的:
std::string
一个;和 char
一个,使用来自标量 operator
之一的隐式转换。尽管后者稍后会引起歧义,但编译器似乎没有达到这一点。
解决方案是进行单个模板化转换
operator
,并使用 SFINAE 将其限制为特定类型:
然后进行以下工作:
template <auto V, typename, typename...>
inline constexpr auto value = V;
template <typename T, typename ...P>
concept one_of = (std::same_as<T, P> || ...);
class Test
{
public:
template <one_of<int, float, std::string> T>
operator T() const
{
if constexpr (std::same_as<T, int>)
return 42;
else if constexpr (std::same_as<T, float>)
return 42;
else if constexpr (std::same_as<T, std::string>)
return "foo";
else
static_assert(value<false, T>, "This shouldn't happen.");
}
};
static_assert
并不是真正必要的,因为我们已经有了requires
。如果您向 requires
添加更多类型并忘记分支,它只会提供更好的错误。
注意
value<false, T>
而不是 false
。尝试直接将 false
放在那里会导致某些编译器无条件地发出错误,即使未采用分支(这是允许的,但不是必需的)。使用依赖于 T
的表达式可以说服编译器延迟测试,直到实际实例化(因为就其所知,对于某些 value
,true
可以专门变成 T
)。
当我在定义它的同一行上分配给 test_str 时
这是初始化,而不是赋值。它调用构造函数,而不是
operator=
。