Vic*_*gue 3 c++ variant crtp c++17
我将以下代码写入名为 的文件中main.cpp。\n它涉及标准类型的奇怪的重复模板模式 (CRTP) std::variant。
#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}\nRun 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 ~~~~~~~~^~\nRun Code Online (Sandbox Code Playgroud)\n我的maybe_either派生类std::variant<...>可以在其他上下文中正常使用,但是当我调用std::visit(...)它时,它无法编译。怎么了?
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)