变体中从基元类型到用户定义类型的隐式转换

Implicit conversion from primitive to user-defined types in variant

提问人:Horus 提问时间:8/22/2023 更新时间:8/22/2023 访问量:63

问:

我有两个类和 hat 模仿各自的基元类型,应该在 .几乎可以编译的示例:IntBoolstd::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 中删除,(3) 可以工作。删除可使 (1) 和 (2) 正常工作。BoolInt

但是当 和 存在时,没有一个有效:IntBool

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 一起使用)

我希望能够将基元类型分配给 并将它们隐式转换为 或 .VariantTIntBool

为什么它不起作用?我怎样才能让它工作?

谢谢!

C++ C++17 标准变体

评论


答:

3赞 Sam Varshavchik 8/22/2023 #1

例如,在 C++ 中,如果函数参数是 ,则可以传入一个值,并进行隐式转换。反之亦然。这会导致一个问题:intbool

VariantT v2 = true;  // (2) does not work

这里有两种可能的、不同的隐式转换。

  1. 使用带有参数的构造函数来构造 ,然后使用 .BooltrueBoolBool

  2. 使用 的构造函数,将 传递转换为 int 值 1 的构造函数,以构造一个 ,然后用 构造 。InttrueIntInt

这两种转换都比另一种转换更可取,因此过载解析会因此而失败。

我怎样才能让它工作?

改变一些东西。重新设计一些东西,做一些事情来禁用隐式转换。一种方法是弄乱构造函数,以防止它们接受隐式转换,并强制它们严格接受相应的类型:

(现场演示)

#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) {}

现在,的构造函数无法解析重载,除非它的参数是实数的、骨架的,而 的构造函数无法重载解析,除非它的参数是实数的、骨架的。BoolboolIntint

请注意,这还可以防止 s、s 和其他整数挤过。一种可能的变化是,您可能想尝试一下。charlong!std::is_same_v<T,bool>