变体访问和common_type

Bér*_*ger 7 c++ variant c++17

我想知道std::visit返回类型转换应该如何工作.

上下文如下:我有一个变体对象,我想std::visit根据它的底层类型应用(通过)不同的函数.每个函数的结果可能有不同的类型,但我希望std :: visit将其打包为变体类型.

伪代码:

我有:

variant<A,B> obj
f(A) -> A
f(B) -> B
Run Code Online (Sandbox Code Playgroud)

我想要:

if obj is of type A => apply f(A) => resA of type A => pack it in variant<A,B>
if obj is of type B => apply f(B) => resB of type B => pack it in variant<A,B>
Run Code Online (Sandbox Code Playgroud)

现在,根据cppreference,std :: visit的返回类型是"所选访问者调用返回的值,转换为所有可能的std :: invoke表达式的公共类型"但是没有指定常见类型的含义.是std::common_type吗?在这种情况下,它不适用于gcc 7.2:

#include <variant>
#include <iostream>
#include <type_traits>

struct A {
    int i;
};
struct B {
    int j;
};

// the standard allows to specialize std::common_type
namespace std {
    template<>
    struct common_type<A,B> {
        using type = std::variant<A,B>;
    };
    template<>
    struct common_type<B,A> {
        using type = std::variant<A,B>;
    };
}


struct Functor {
    auto
    operator()(A a) -> A {
        return {2*a.i};
    }
    auto
    operator()(B b) -> B {
        return {3*b.j};
    }
};


int main() {
    std::variant<A,B> var = A{42};

    auto res = std::visit( Functor() , var ); // error: invalid conversion from 'std::__success_type<B>::type (*)(Functor&&, std::variant<A, B>&) {aka B (*)(Functor&&, std::variant<A, B>&)}' to 'A (*)(Functor&&, std::variant<A, B>&)' [-fpermissive]

}
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能表达这种解压 - 应用访问 - 重新包装模式?

笔记:

1)专业std::common_type<A(*)(Ts...),B(*)(Ts...)>不会削减它.这可以解决问题,但依赖于特定的std :: lib实现细节.此外,它不适用于多次访问.

2)我给出的例子实际上已经减少到最低限度,但你必须想象我想要提供的访问机制是在库侧,访问者是在客户端,可以是任意复杂的:未知数和参数类型,未知的返回类型.该库应该只提供访问和一组预定义的std::common_type专业化,用于访问返回类型.例如,定义

auto f = [](auto x) -> variant<A,B> { return Functor()(x); };
Run Code Online (Sandbox Code Playgroud)

然后应用std::visitf是不是一个可行的选择:从库边,我不能在不知道"打包"返回类型预先定义这种拉姆达的.[主要的问题是,我认为无法询问std::common_type特定过载集的语言]

Jar*_*d42 5

您可以创建自己的visit图层,例如:

template <typename Visitor, typename ... Ts>
decltype(auto) my_visit(Visitor&& vis, const std::variant<Ts...>& var)
{
    return std::visit([&](auto&& e)
        -> std::common_type_t<decltype(vis(std::declval<Ts>()))...>
        {
            return vis(e);
        }, var);
}
Run Code Online (Sandbox Code Playgroud)

演示