我有两个类
Int
和 Bool
模仿各自的原始类型,应该在 std::variant
中使用。几乎可以编译的例子:
#include <iostream>
#include <string>
#include <variant>
using namespace std;
class Bool {
public:
Bool(bool val) : m_value(val) {}
operator bool() const { return m_value; }
private:
bool m_value{false};
};
class Int {
public:
Int(int val) : m_value(val) {}
operator int() const { return m_value; }
private:
int m_value{0};
};
int main() {
using VariantT = std::variant<std::string, Bool, Int>;
Bool b1 = true;
VariantT v1 = b1; // (1) works
VariantT v2 = true; // (2) does not work
VariantT v3 = 1; // (3) does not work
return 0;
}
如果我从 VariantT 中删除
Bool
,则 (3) 有效。删除 Int
使 (1) 和 (2) 起作用。
但是当
Int
和 Bool
存在时,没有任何作用:
test.cpp:32:12: error: no viable conversion from 'bool' to 'VariantT' (aka 'variant<basic_string<char>, Bool, Int>')
VariantT v2 = true; // (2) does not work
^ ~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/variant:1365:7: note: candidate constructor not viable: no known conversion from 'bool' to 'const variant<basic_string<char>, Bool, Int> &' for 1st argument
variant(const variant& __rhs) = default;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/variant:1366:7: note: candidate constructor not viable: no known conversion from 'bool' to 'variant<basic_string<char>, Bool, Int> &&' for 1st argument
variant(variant&&) = default;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/variant:1378:2: note: candidate template ignored: requirement '18446744073709551615UL < sizeof...(_Types)' was not satisfied [with _Tp = bool, $1 = enable_if_t<sizeof...(_Types) != 0>, $2 = enable_if_t<__not_in_place_tag<bool>>]
variant(_Tp&& __t)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/variant:1388:2: note: explicit constructor is not a candidate
variant(in_place_type_t<_Tp>, _Args&&... __args)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/variant:1408:2: note: explicit constructor is not a candidate
variant(in_place_index_t<_Np>, _Args&&... __args)
(使用 clang++ 16 和 c++17)
我希望能够将原始类型分配给
VariantT
并将它们隐式转换为 Int
或 Bool
。
为什么不起作用?我怎样才能让它发挥作用?
谢谢!
在 C++ 中,例如,如果函数参数是
int
,则可以传入 bool
值,并且会发生隐式转换。反之亦然。这会导致一个问题:
VariantT v2 = true; // (2) does not work
这里有两种可能的、不同的隐式转换。
使用带有
Bool
参数的 true
构造函数来构造 Bool
,然后使用您的 Bool
构造变体。
使用
Int
的构造函数,将转换为int值1的true
传递给构造函数,构造一个Int
,然后用Int
构造变体。
这两种转换都不优于另一种,因此重载解析因此失败。
我怎样才能让它发挥作用?
改变一些东西。重新设计一些东西,做一些事情来禁用隐式转换。一种方法是搞乱构造函数以防止它们接受隐式转换,并强制它们严格接受相应的类型:
#include <type_traits>
// ...
template<typename T, typename=std::enable_if_t<std::is_same_v<T, bool>>>
Bool(const T &val) : m_value(val) {}
// ...
template<typename T, typename=std::enable_if_t<std::is_same_v<T, int>>>
Int(const T &val) : m_value(val) {}
现在,
Bool
的构造函数将无法重载解析,除非其参数是真实的、真实的 bool
,而 Int
的构造函数将无法重载解析,除非其参数是真实的、真实的 int
。
请注意,这也可以防止
char
、long
和其他整数挤过。一种可能的变体是 !std::is_same_v<T,bool>
,您可能想尝试一下。