【问题标题】:Variant with variadic templates带有可变参数模板的变体
【发布时间】:2021-04-07 14:20:01
【问题描述】:

对不起,如果这个案例相当复杂,但我希望它可以帮助人们更好地理解现代 c++ 的用法。所以我想让这段代码工作。它应该为单个整数类型和变体类型生成特殊的 lambda,用于计算硬静态转换为单一类型或软变体转换为通用类型的术语序列,而这些术语将被使用。我添加了 cmets,它描述了我在这段代码中真正尝试做的事情。

#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>();
}

【问题讨论】:

  • 请说明此代码当前如何无法工作,即编译器错误消息或错误行为。
  • 取决于编译器,我尝试在这里了解所有问题范围:godbolt.org/z/3coPrrn5c
  • 如果编译器的行为不同,这是有用的信息。请添加这些详细信息,并在问题中执行此操作,而不是 cmets。
  • C++17 还是 C++20?
  • 我还在用 C++17

标签: c++ c++17 variadic-templates template-meta-programming std-variant


【解决方案1】:

我没有办法让您的代码正常工作...

无论如何,一些问题/建议

  1. 也许您的Visitor 可以工作,但您可以使用经典方法做得更好:可变参数继承
template <typename ... Ts>
struct Visitor : public Ts...
 { using Ts::operator()...; };

并避免专业化和递归

  1. 当您打电话时
auto sm = term::legion::sum<int>();

你有,在sum() 里面(假设Vs...int),

Calculator<int>::template variant_sum<int>

那个调用,在variant_sum 内部(假设Vs...int),

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

variant_value&lt;int&gt;(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()...; };

这样Visitor&lt;int&gt; 继承自foo&lt;int&gt;(不是从int)和foo&lt;int&gt; 作为operator() 接受int

这是访客,但你问

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

对于包含的值。

  1. 大问题来了:你不能从std::visit 获得价值。

或者更好......来自std::variant&lt;int&gt;(只有一个类型的变体),你可以。可以修改foo&lt;T&gt;如下

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&lt;char, std::int16_t, double&gt;) 包含 三个 不同的 operator() 函数,返回 三个 不同的类型(charstd::int16_tdouble )。

std::visit() 必须返回单一类型。根据变体中活动的类型,没有不同的类型。

所以我认为您的代码必须完全重新考虑。

其他小问题

  1. 以下声明适用于 C++20
template<typename T>
function<any(any)> legion(auto& algebra) noexcept

但在 C++17 中,您不能使用 auto 作为参数类型的占位符(通用 lambda 除外);正确的 C++17 方式通过一个额外的模板参数,所以

template <typename T, typename U>
function<std::any(std::any)> legion (U & algebra) noexcept
  1. sumvariant_sumCalculator内添加constexpr
static constexpr auto sum = ...

static constexpr auto variant_sum = ...
  1. 如果Calculator 仅有public 成员,则将其设为struct

【讨论】:

  • 谢谢 我想出了几个方面,我还不清楚我是否可以传递对静态模板函数的引用,或者我是否应该以调用静态函数的方式设计所有内容每次产生一个新的 lambda。
猜你喜欢
  • 2014-09-08
  • 2016-06-05
  • 2015-12-14
  • 2015-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 1970-01-01
相关资源
最近更新 更多