【问题标题】:Using std::visit on a class inheriting from std::variant - libstdc++ vs libc++在从 std::variant 继承的类上使用 std::visit - libstdc++ vs libc++
【发布时间】:2018-12-20 21:51:30
【问题描述】:

考虑以下代码sn-p:

struct v : std::variant<int, std::vector<v>> { };

int main()
{
    std::visit([](auto){ }, v{0});
}
  • clang++ 7 和-stdlib=libc++ -std=c++2a 编译代码;

  • g++ 9 with -std=c++2a 编译代码失败,报错如下:

    /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;
    
                             ^~~~~~~~~~~~~~
    

live example on godbolt.org


  • 两种实现都符合标准吗?

  • 如果不是,这里什么实现是正确的,为什么?

【问题讨论】:

  • 介意解释否决票吗?
  • 不是反对者,但-std=c++2a 不是 C++17,它是尚未完成的 C++20。你是想问这个吗?
  • 它与“递归变体”无关,从简单的std::variant&lt;int&gt;继承产生相同的问题Demo
  • downvote 和 close vote 对我来说完全无法解释。
  • 显然两者都不符合不存在的c++2a标准;)

标签: c++ language-lawyer c++17 variant


【解决方案1】:

[variant.visit] in C++17 不使用variant_size_v,但由于editorial change,它在current working draft 中使用。我没有看到任何迹象表明 LWG 在更改之前对其进行了审查,但是从那以后它已经多次查看标准的这一部分并且尚未反对它,所以我假设它在事实要求。

同时,LWG issue 3052(已提交给 LEWG)将明确要求 std::variant。当这个问题得到解决时——一种或另一种方式——它也应该解决这个问题。

【讨论】:

  • FWIW:Thomas 和我认为这个更改是编辑性的,因为“所有变体”具有“std::variant 的专业化”的明显含义。 LWG 将 3052 提交给 LEWG 这一事实向我表明,我们不确定“变体”一词的含义。
【解决方案2】:

看起来这是 gcc 实现中的一个错误。根据cppreference,调用它就像在std::get 上调用invokestd::get&lt;&gt; 定义为可转换为 std::variant 的任何内容(因为它通过转发引用接受 std::variant 参数)。您的结构可以转换为std::variant,因此std::get 本身适用于您在gcc 中的结构。

gcc 实现选择使用std::variant_size 作为其实现visit 的一部分这一事实是它们的实现细节,而它不(也不应该)为您的结构工作的事实是无关紧要的.

结论:这是gcc中的一个错误,由于实施过程中的疏忽。

【讨论】:

  • 引用 cppreference 来表示“这是否合法”是有问题的;在标记为 [language-lawyer] 的问题中,这是完全错误的。 cppreference 包含合理的“如何使用”信息;对于实施怪癖,它包含“我们对孩子说的谎言”。
  • @Yakk-AdamNevraumont,我同意,但为了方便起见,我将其列出。 C++ 17 标准在这方面没有什么不同,它实际上具有相同的措辞。
【解决方案3】:

我最近也遇到了这个问题。我想出了一个解决方法,它基本上专门针对从变体继承的类的 variant_size 和 variant_alternative..

link on godbolt

它并不漂亮,它会将东西注入 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-20
    • 2020-01-03
    • 2021-03-25
    • 1970-01-01
    • 1970-01-01
    • 2017-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多