请考虑以下代码段:
struct v : std::variant<int, std::vector<v>> { };
int main()
{
std::visit([](auto){ }, v{0});
}
使用-stdlib=libc++ -std=c++2a
的clang ++ 7编译代码;
-std=c++2a
的g ++ 9无法编译代码,并出现以下错误:
/ opt / compiler-explorer / gcc-trunk-20180711 / include / c ++ / 9.0.0 / variant:94:29: 错误:嵌套名称中使用了不完整的类型'std :: variant_size' 说明符
inline constexpr size_t variant_size_v = variant_size<_Variant>::value; ^~~~~~~~~~~~~~
两个实现均符合标准吗?
如果不是,什么实现在这里正确,为什么?
答案 0 :(得分:12)
[variant.visit] in C++17不使用variant_size_v
,但由于current working draft而在editorial change中使用。我看不到任何迹象表明LWG在进行更改之前已经对其进行了审核,但是自那以后它已经对该标准的这一部分进行了多次考察,并且尚未反对它,因此我将假定它已经在需要事实。
与此同时,已被称为LEWG的LWG issue 3052将明确要求std::variant
。解决该问题(一种方法或另一种方法)后,它也应解决此问题。
答案 1 :(得分:8)
看起来这是gcc实现中的错误。根据{{3}},它的调用就像在invoke
上调用std::get
一样。 std::get<>
定义为可转换为std::variant
的任何内容(因为它通过转发引用接受std::variant
自变量)。您的结构可以转换为std::variant
,因此std::get
本身可以在gcc中对您的结构起作用。
gcc实现选择使用std::variant_size
作为其visit
实现的一部分的事实是它们的实现细节,并且它不(也不应该)用于您的结构无关紧要。
结论:由于实施过程中的疏忽,这是gcc中的错误。
答案 2 :(得分:1)
最近我也遇到了这个问题。我想出了一种解决方法,该方法基本上专门针对从变体继承的类专门设计了variant_size和variant_alternative。
它不漂亮,并且将东西注入到std名称空间中。我还不是元编程专家(至今!),所以我一起砍掉了它。也许其他人可以对此进行改进?
#include <variant>
#include <string>
#include <vector>
#include <iostream>
#include <utility>
#include <type_traits>
using var = std::variant<int, bool, float, std::string>;
struct myvar : public var {
using var::var;
using var::operator=;
};
namespace std{
template<>
struct variant_size<myvar> : variant_size<var> {
};
template<std::size_t I>
struct variant_alternative<I,myvar> : variant_alternative<I,var> {
};
}
int main(){
constexpr int vs = std::variant_size<var>::value;
myvar s = std::string{"boo!"};
std::visit([](auto&& e){std::cout << e << "\n";}, s);
std::cout << vs;
}