std::variant 个包含自身的容器

std::variant of a container that contains itself

我有一种二进制格式,我正在为其编写编码器和解码器。几乎所有二进制类型都直接映射到基元,除了两种容器类型,一个列表和一个映射类型,它们可以包含格式中的任何其他类型,包括它们自身。

这些感觉就像他们只是想成为 std::variant

的 typedef
typedef std::variant<std::vector<char>, std::vector<int>, ...> ListType

但是因为我需要能够包含 ListType 本身的向量,所以我最终这样做了

struct ListType {
  std::variant<std::vector<char>, std::vector<int>, ..., std::vector<ListType>> value;
}

这给使用该类型增加了一点摩擦。这些变量实际上没有其他状态来证明封装它们是合理的。

打出来我意识到我在问“你能转发声明一个模板吗?”这似乎是一个愚蠢的问题。尽管如此,有人对此有更好的策略吗?

template<class...Ts>
struct self_variant;

template<class...Ts>
using self_variant_base = 
  std::variant<
    std::vector<Ts>...,
    std::vector<self_variant<Ts...>>
  >;


template<class...Ts>
struct self_variant:
  self_variant_base<Ts...>
{
  using self_variant_base<Ts...>::self_variant_base;
  self_variant_base<Ts...> const& base() const { return *this; }
  self_variant_base<Ts...>& base() { return *this; }
};

template<class T>
void print( T const& t ) {
    std::cout << t << ",";
}
template<class T>
void print( std::vector<T> const& v ) {
    std::cout << "[";
    for (auto const& e:v) {
        print(e);
    }
    std::cout << "]\n";
}
template<class...Ts>
void print( self_variant<Ts...> const& sv ) {
    std::visit( [](auto& e){
        print(e);
    }, sv.base());
}

int main() {
    self_variant<int, char> bob = std::vector<int>{1,2,3};
    self_variant<int, char> alice = std::vector<self_variant<int, char>>{ bob, bob, bob };
    print(alice);
}

所以,需要.base()是因为std::visit措辞有点错误。我相信这将在未来的标准修订中得到解决。

无论如何,这会减少一些摩擦。

Live example, 3 recursive depth live example.