В C++, как сделать вариант, который может содержать вектор того же варианта?

Я пытаюсь сделать Std:: вариант, который может содержать вектор того же варианта:

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

Я получаю переопределение ScriptParameter. Возможно, это потому, что параметр шаблона не может быть объявлен вперед?

Есть ли способ получить вариант, который также может содержать массив одинаковых типизированных вариантов?

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

Используйте оператор с фиксированной точкой уровня типа.

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

Я не уверен, что рекурсивное определение имеет смысл в этом случае. Это позволяет произвольно использовать много вложенных векторов внутри одного 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 ниже.

Другие вопросы по тегам