【问题标题】:Incomplete type `std::variant<...>` used in nested name specifier嵌套名称说明符中使用的不完整类型`std::variant<...>`
【发布时间】:2020-08-27 13:08:49
【问题描述】:

我将以下代码写入名为main.cpp 的文件中。 它涉及具有标准类型std::variant 的奇怪重复模板模式(CRTP)。

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

template<typename T>
struct either {
    std::vector<T> arg;
};

template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {

    template<typename U>
    maybe_either(U&& v):
      std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
    }
};

struct var {
  std::string name;
};

int main(int, char**) {
    auto expression = maybe_either<var>(either<maybe_either<var>>{});
    std::visit([&](auto&& v) {
        using T = std::decay_t<decltype (v)>;
        if constexpr (std::is_same_v<T, var>) {
          // ...
        } else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
          // ...
        }
    }, expression);
    return 0;
}

使用以下命令行编译时,我收到以下错误消息:

$ g++ -c -std=c++17 main.cpp
In file included from main.cpp:2:0:
/usr/include/c++/7/variant: In instantiation of ‘constexpr const size_t std::variant_size_v<maybe_either<var> >’:
/usr/include/c++/7/variant:702:10:   required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23:   required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18:   required from here
/usr/include/c++/7/variant:97:29: error: incomplete type ‘std::variant_size<maybe_either<var> >’ used in nested name specifier
     inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
                             ^~~~~~~~~~~~~~
/usr/include/c++/7/variant: In instantiation of ‘constexpr const auto std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>::_S_vtable’:
/usr/include/c++/7/variant:711:29:   required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23:   required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18:   required from here
/usr/include/c++/7/variant:711:49: error: ‘_S_apply’ was not declared in this scope
       static constexpr auto _S_vtable = _S_apply();
                                         ~~~~~~~~^~

我的类maybe_either 派生自std::variant&lt;...&gt; 可以在其他上下文中正常使用,但是当我在其上调用std::visit(...) 时,它无法编译。怎么了?

【问题讨论】:

  • 我会尝试,但是这个限制是否在某个地方解释过?这只是一个已知问题吗?
  • @AndyG 在这种情况下不是问题,因为另一种选择是either&lt;incomplete&gt;,它有一个成员vector&lt;incomplete&gt;,而vector 允许这些恶作剧。基本上你是对的,“在某些时候你需要使用指针”——这只是 OP 已经是。

标签: c++ c++17 variant crtp


【解决方案1】:

这基本上是LWG3052,我试图在P2162 中解决。

maybe_either&lt;T&gt; 不是std::variant 的特化——它继承自一个。而std::visit 目前未指定。目前还不清楚允许访问哪些类型的“变体”。

libstdc++ 实现了该库问题中的原始建议方向,std::variant 的特化(你不是)。另一方面,libc++ 允许访问从std::variant 继承的类型,因此它是accepts your example

其目的是让示例最终变得格式良好。但在那之前,您必须确保您所做的访问是直接在std::variant 上进行的。您可以通过添加自己的成员或非成员 visit 来为您执行此转换,因此调用者不必自己进行。

例如,这个:

template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
    using base = typename maybe_either::variant;

    template<typename U>
    maybe_either(U&& v):
      std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
    }

    template <typename F>
    decltype(auto) visit(F&& f) & {
        return std::visit(std::forward<F>(f), static_cast<base&>(*this));
    }
};

允许to work:

int main(int, char**) {
    auto expression = maybe_either<var>(either<maybe_either<var>>{});
    expression.visit([&](auto&& v) {
        using T = std::decay_t<decltype (v)>;
        if constexpr (std::is_same_v<T, var>) {
          // ...
        } else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
          // ...
        }
    });
    return 0;
}

【讨论】:

  • 感谢@Barry 的回答,我理解得很好,它就像一个魅力!我希望 P2162 能被接受。
猜你喜欢
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
  • 2018-05-26
  • 2021-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多