嵌套名称说明符中使用的不完整类型 `std::variant<...>`

Vic*_*gue 3 c++ variant crtp c++17

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

\n
#include <string>\n#include <variant>\n#include <vector>\n\ntemplate<typename T>\nstruct either {\n    std::vector<T> arg;\n};\n\ntemplate<typename T>\nstruct maybe_either: std::variant<T, either<maybe_either<T>>> {\n\n    template<typename U>\n    maybe_either(U&& v):\n      std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {\n    }\n};\n\nstruct var {\n  std::string name;\n};\n\nint main(int, char**) {\n    auto expression = maybe_either<var>(either<maybe_either<var>>{});\n    std::visit([&](auto&& v) {\n        using T = std::decay_t<decltype (v)>;\n        if constexpr (std::is_same_v<T, var>) {\n          // ...\n        } else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {\n          // ...\n        }\n    }, expression);\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

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

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

\n

Bar*_*rry 5

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

maybe_either<T>不是一个专业化std::variant- 它继承自一个。目前尚未std::visit明确。目前还不清楚允许访问哪些类型的“变体”。

libstdc++ 实现了该库问题中最初建议的方向,这只是std::variant您不是的)的专业化。另一方面,libc++ 允许std::variant访问继承自的类型,因此它接受您的示例

目的是使示例最终变得格式良好。但在那之前,您必须确保您所做的访问是直接在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));
    }
};
Run Code Online (Sandbox Code Playgroud)

允许这个工作

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;
}
Run Code Online (Sandbox Code Playgroud)