提问者:小点点

带有variadic模板的Variant


如果这个例子比较复杂,我很抱歉,但我希望它能帮助人们更好地理解现代C++用法。所以我想让这段代码工作起来。它应该为单积分型和变分型产生特殊的lambdas,以便在硬静态转换为单积分型或软变分转换为普通型时计算项的顺序。我添加了注释,这些注释描述了我在这段代码中真正尝试做的事情。

#include <variant>
#include <type_traits>
#include <string>
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <any>
#include <utility>

/* Visitor */ 
// Should get values from a variant
template <class... Ts>
struct Visitor;

template <class T, class... Ts>
struct Visitor<T, Ts...> : T, Visitor<Ts...> {
    Visitor(T t, Ts... rest) : T(t), Visitor<Ts...>(rest...) {}
    using T::operator();
    using Visitor<Ts...>::operator();
};

template <class T>
struct Visitor<T> : T {
    Visitor(T t) : T(t) {}
    using T::operator();
};

/* Calculator */
// Should get special lambda for same type integrals and for variant packed integrals
template<class T, class... Ts>
class Calculator {
public:

    // lambda for simple calculate same type integrals
    static auto sum = [](T a, T b) { return a + b; };

    // api for get current value of variant
    template<typename... Vs>
    static auto variant_value(std::variant<Vs...> v) {
        return std::visit(Visitor<Vs...>{}, v);
    }

    // lambda for unpack variant arguments calc common value and pack in variant again
    template<typename... Vs>
    static auto variant_sum = [](std::variant<Vs...> a, std::variant<Vs...> b) {
        auto value = variant_value<Vs...>(a) + variant_value<Vs...>(b);
        return std::variant<Vs...>(value);
    };
    
};

/* Term Producer */
namespace term::legion {

    using std::function, std::any;

    template<typename T>
    function<any(any)> legion(auto& algebra) noexcept { // std::function<T(T,T...)
        function<any(any)> redex = [](std::any a) {return a;};
        // I delete code here because its not important now
       return redex;
    }

    // production lamda for single type values 
    template<typename T>
    std::function<std::any(std::any)> sum(T arg) noexcept {
        return legion<T>(Calculator<T>::sum);
    };

    // production lambda for variant type values
    template<typename ...Vs>
    std::function<std::any(std::any)> sum() noexcept {
        std::cout << "variant sum selected" << std::endl;
        return legion<std::variant<Vs...>>(Calculator<Vs...>::template variant_sum<Vs...>);
    };

}

int main() {
     // term contains lambda for single type  
     auto sm = term::legion::sum<int>();
     // term contains lambda for variant type 
     auto v_sm = term::legion::sum<char, int16_t, double>();
}

共1个答案

匿名用户

我看不出有什么方法能让你的代码正常运行...

总之,有些问题/建议

>

  • 也许您的访问者可以工作,但您可以使用经典的方法:变量继承来更好地实现

    模板结构访问者:公共TS...{使用ts::运算符()...;};

    并避免专门化和递归

    当你打电话的时候

    自动sm=术语::军团::SUM();

    sum()(假定vs...int)中,

    Calculator<int>::template variant_sum<int>
    

    variant_sum(假定vs...int)内部的调用,

    variant_value<int>(a) + variant_value<int>(b);
    

    variant_value(a)(假定vs...int)内实例化

     Visitor<int>
    

    问题是,visitor继承自的是模板参数,类不能继承自int

    要创建一个访问者,您需要与所有类型的变体一起工作的函数,但您必须稍微工作一些;例如,您可以编写一种操作符包装,如下所示

    template <typename T>
    struct foo 
     { void operator() (T const &) { } };
    

    和访问者如下

    template <typename ... Ts>
    struct Visitor : public foo<Ts>...
     { using foo<Ts>::operator()...; };
    

    这样,visitorfoo继承(而不是从int)和foo作为接受int运算符()

    这可以作为访客但你问

    variant_value<Vs...>(a) + variant_value<Vs...>(b);
    

    对于包含的值。

    或者更好...从std::variant(只有一个类型的变量)中,您可以。您可以修改foo,如下所示

    template <typename T>
    struct foo 
     { T operator() (T const & val) { return val; } };
    

    还有(还有一些其他的修正)这个电话

    auto sm = term::legion::sum<int>();
    

    编译。

    但是下面的sum()

    auto v_sm = term::legion::sum<char, int16_t, double>();
    

    因为结果的访问者(visitor)包含三个不同的operator()函数,返回三种不同的类型(charstd::int16_tdouble)。

    但是std::visit()必须返回一个类型。而不是根据变体中活动的类型而不同的类型。

    所以我想你的代码应该重新考虑一下。