使用 std::variant 作为 class 会员并申请访问者
Use std::variant as class member and apply visitor
我正在尝试将std::variant用作class成员变量,然后使用运算符重载,以便此class的两个Variants
可以使用运算符plus
产生一个新的变量。问题是 std::get 不像我想的那样工作,所以我无法检索正确的(硬编码)字符串类型,因此 AddVisitor
结构被使用。
我收到一个编译错误:no matching function for call to ‘get<0>(std::basic_string&)’
还有没有 operator+
函数可以在没有 if-else
语句的情况下检测类型的方法?
我已经在 SO 中检查了很多答案,包括回答有关类似 Boost 功能的问题的答案,但我无法让它工作。
#include <iostream>
#include <variant>
#include <string>
#include "stdafx.h"
using Variant = std::variant<int, std::string>;
template<typename T>
struct AddVisitor
{
T operator()(T v1, T v2)
{
return v1 + v2;
}
};
class Var
{
Variant v;
public:
template<typename T>
Var(T value) : v(value) {}
Var operator+(Var& val)
{
// PROBLEM: This is a hard coded example that I want to use, so that concatenation of two strings happens.
return std::visit(AddVisitor<std::string>(), std::get<std::string>(v), std::get<std::string>(val.get()));
// Is there a way to get the correct type without if-else statements here?
}
Variant get()
{
return v;
}
};
int main()
{
Var x("Hello "), y("World");
// The expected output is this:
Var res = x + y;
return 0;
}
我希望能够使用加号运算符连接两个字符串或两个整数并创建一个新的 Var
变量。
好的,有几件事要谈。
首先,具有多个变体参数的 std::visit
的访问者应该接受变体类型的所有组合。在你的情况下,它应该接受:
(string, string)
(string, int)
(int, int)
(int, string)
如果只有 string, string
和 int, int
有效,您仍然需要接受其他组合才能编译代码,但您可以将它们放入。
接下来,访问者不应被模板化。相反,operator()
应该为上述所有组合进行模板化或重载。
所以这里是 AddVisitor
:
struct AddVisitor
{
auto operator()(const std::string& a, const std::string& b) const -> Variant
{
return a + b;
}
auto operator()(int a, int b) const -> Variant
{
return a + b;
}
// all other overloads invalid
template <class T, class U>
auto operator()(T, U) const -> Variant
{
throw std::invalid_argument{"invalid"};
}
};
从文档中不清楚重载可以做什么 return,但除非全部 return Variant
,否则我无法编译它。幸运的是,编译器错误是 TREMENDOUSLY HELPFULL 。 (我需要检查标准)。
接下来,当您调用 std::visit
时,您需要传递您拥有的 variant
。
所以最后的代码是这样的:
auto operator+(Var& val) -> Var
{
return std::visit(AddVisitor{}, get(), val.get());
}
而且您确实可以随心所欲地使用它:
Var res = x + y;
您的代码的另一个问题是 get
进行了不必要的复制。 std::variant
的副本制作起来并不便宜。所以我建议:
auto get() const -> const Variant& { return v; }
auto get() -> Variant& { return v; }
我正在尝试将std::variant用作class成员变量,然后使用运算符重载,以便此class的两个Variants
可以使用运算符plus
产生一个新的变量。问题是 std::get 不像我想的那样工作,所以我无法检索正确的(硬编码)字符串类型,因此 AddVisitor
结构被使用。
我收到一个编译错误:no matching function for call to ‘get<0>(std::basic_string&)’
还有没有 operator+
函数可以在没有 if-else
语句的情况下检测类型的方法?
我已经在 SO 中检查了很多答案,包括回答有关类似 Boost 功能的问题的答案,但我无法让它工作。
#include <iostream>
#include <variant>
#include <string>
#include "stdafx.h"
using Variant = std::variant<int, std::string>;
template<typename T>
struct AddVisitor
{
T operator()(T v1, T v2)
{
return v1 + v2;
}
};
class Var
{
Variant v;
public:
template<typename T>
Var(T value) : v(value) {}
Var operator+(Var& val)
{
// PROBLEM: This is a hard coded example that I want to use, so that concatenation of two strings happens.
return std::visit(AddVisitor<std::string>(), std::get<std::string>(v), std::get<std::string>(val.get()));
// Is there a way to get the correct type without if-else statements here?
}
Variant get()
{
return v;
}
};
int main()
{
Var x("Hello "), y("World");
// The expected output is this:
Var res = x + y;
return 0;
}
我希望能够使用加号运算符连接两个字符串或两个整数并创建一个新的 Var
变量。
好的,有几件事要谈。
首先,具有多个变体参数的 std::visit
的访问者应该接受变体类型的所有组合。在你的情况下,它应该接受:
(string, string)
(string, int)
(int, int)
(int, string)
如果只有 string, string
和 int, int
有效,您仍然需要接受其他组合才能编译代码,但您可以将它们放入。
接下来,访问者不应被模板化。相反,operator()
应该为上述所有组合进行模板化或重载。
所以这里是 AddVisitor
:
struct AddVisitor
{
auto operator()(const std::string& a, const std::string& b) const -> Variant
{
return a + b;
}
auto operator()(int a, int b) const -> Variant
{
return a + b;
}
// all other overloads invalid
template <class T, class U>
auto operator()(T, U) const -> Variant
{
throw std::invalid_argument{"invalid"};
}
};
从文档中不清楚重载可以做什么 return,但除非全部 return Variant
,否则我无法编译它。幸运的是,编译器错误是 TREMENDOUSLY HELPFULL 。 (我需要检查标准)。
接下来,当您调用 std::visit
时,您需要传递您拥有的 variant
。
所以最后的代码是这样的:
auto operator+(Var& val) -> Var
{
return std::visit(AddVisitor{}, get(), val.get());
}
而且您确实可以随心所欲地使用它:
Var res = x + y;
您的代码的另一个问题是 get
进行了不必要的复制。 std::variant
的副本制作起来并不便宜。所以我建议:
auto get() const -> const Variant& { return v; }
auto get() -> Variant& { return v; }