【问题标题】:In C++, how to make a variant that can contain a vector of of same variant?在 C++ 中,如何制作一个可以包含相同变体向量的变体?
【发布时间】:2019-04-29 09:01:24
【问题描述】:

我正在尝试制作一个可以包含相同变体向量的 std::variant:

class ScriptParameter;
using ScriptParameter = std::variant<bool, int, double, std::string, std::vector<ScriptParameter> >;

我正在重新定义 ScriptParameter。它认为可能是因为模板参数无法前向声明?

有没有办法实现一个变体,它也可以包含一组相同类型的变体?

【问题讨论】:

  • 这听起来更像是您设计中的一个更普遍的缺陷。你真正想达到什么目标?
  • 我认为 C++ 不支持先有鸡还是先有蛋的模式。
  • 看起来像是命名冲突?您期望编译器将ScriptParameter 理解为什么?类型,还是前向声明的类?
  • @Jodocus 看起来他正在制作一个复合模式节点。我真的很喜欢这个主意。

标签: c++ vector c++17 self-reference std-variant


【解决方案1】:

我不确定在这种情况下递归定义是否有意义。它允许在单个ScriptParameter 内任意多个嵌套向量。 (本质上,我们是说脚本参数是单个值或整个 森林 值。)将定义一分为二可能会更好:

// Represents the value of a single parameter passed to a script
using ScriptParameter = std::variant<bool, int, double, std::string>;

// Represents a collection of one or many script parameters
using ScriptParameterSet = std::variant<ScriptParameter, std::vector<ScriptParameter>>;

或者,如果这里的目标是将参数定义为一组选项中的一个加上这些相同选项的向量,您可以尝试一些模板魔术:

template <class T, class U> struct variant_concat;

template <class... T, class U> struct variant_concat<std::variant<T...>, U>
{
  using type = std::variant<T..., U>;
};

template <class T, class U> using variant_concat_t = typename variant_concat<T, U>::type;

using PrimitiveScriptParameter = std::variant<bool, int, double, std::string>;

using ScriptParameter = variant_concat_t<
  PrimitiveScriptParameter,
  std::vector<PrimitiveScriptParameter>>;

这应该解决以下 Lightness 的可用性问题。

【讨论】:

  • 这并不能真正回答问题,我不确定我们是否可以说 OP 的方法不适合他们的用例。我目前正在使用可以完成他们正在做的事情的代码,这很有意义。您的解决方案稍微简化了类型定义,但会使使用/访问者变得相当复杂。
  • 很难说,因为 OP 拒绝提供上下文。尽管如此,参数的递归定义对我来说似乎有点不对劲,所以我想我会提供这个。
  • 不错不错。 ^_^
【解决方案2】:

使用类型级别fixed-point operator

#include <vector>
#include <variant>
#include <string>

// non-recursive definition 
template<class T>
using Var = std::variant<int, bool, double, std::string, std::vector<T>>;

// tie the knot
template <template<class> class K>
struct Fix : K<Fix<K>>
{
   using K<Fix>::K;
};

using ScriptParameter = Fix<Var>;

// usage example    
int main()
{
    using V = std::vector<ScriptParameter>;
    ScriptParameter k {V{1, false, "abc", V{2, V{"x", "y"}, 3.0}}};
}

【讨论】:

  • 你让我头晕目眩。
【解决方案3】:

由于前向声明说ScriptParameter 是一个类,你不能使用using 别名。但是,这里并没有本质上的错误,因为vector 只是一个指针,没有真正的循环依赖。

你可以使用继承:

class ScriptParameter;
class ScriptParameter
    : public std::variant<bool, int, double, std::string, std::vector<ScriptParameter> >
{
public:
    using base = std::variant<bool, int, double, std::string, std::vector<ScriptParameter> >;
    using base::base;
    using base::operator=;
};

int main() {    
    ScriptParameter sp{"hello"};
    sp = 1.0;
    std::vector<ScriptParameter> vec;
    sp = vec;    
    std::cout << sp.index() << "\n";  
}

【讨论】:

  • 跟我四个星期前所做的完全一样。可悲的是,制作相同的std::map/std::set 是不可能的。
  • 最后,一个有用的答案。谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-01-30
  • 2020-10-08
  • 1970-01-01
  • 2012-03-21
  • 2016-12-28
  • 2021-05-08
  • 2020-06-15
  • 1970-01-01
相关资源
最近更新 更多