在C++中,如何创建一个变量,它可以包含同一变量类型的向量?

14

我正在尝试创建一个能够包含相同变量类型的向量的std::variant:

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

我遇到了ScriptParameter重新定义的问题。我认为这可能是由于无法提前声明模板参数导致的?

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


这听起来更像是你设计中的一个通用缺陷。你真正想要实现什么? - Jodocus
1
我认为C++不支持鸡和蛋模式。 - Eljay
1
看起来是命名冲突了?你希望编译器将 ScriptParameter 理解为什么?类型还是前向声明的类? - Jonathan Mee
1
related/dupe: https://dev59.com/OFkS5IYBdhLWcg3wzJP3 - NathanOliver
这个数组应该是不可能的。那么像这样的东西的大小会是多少呢? - patatahooligan
1
@Jodocus 看起来他正在制作一个组合模式节点。我实际上很喜欢这个想法。 - ravnsgaard
3个回答

14

由于前向声明表示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";  
}

1
很遗憾,无法创建相同的std::map/std::set。这正是我四周前所做的。 - Lightness Races in Orbit
终于有一个有用的答案了。谢谢! - Zuppa

12

使用类型级不动点运算符

#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}}};
}

2
你让我感到头晕。 - Willem Hengeveld
1
哎呀,这真的编译通过了。 - QCTDev

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的方法不适合他们的用例。我目前正在使用与他们所做的相同的代码,这是有道理的。你的解决方案在一定程度上简化了类型定义,但会使使用/访问者变得更加复杂。 - Lightness Races in Orbit
很难说因为OP拒绝提供上下文。尽管如此,参数的递归定义对我来说似乎有点不太对,所以我想提供一下我的看法。 - Peter Ruderman
不错,棒棒哒。^_^ - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接