C++17 如何使用可变参数模板模仿 Julia 的 'promote_type' 函数

C++17 How to mimic Julia's 'promote_type' function using variadic template

出于某些个人原因,我正在开发 Julia 的 SparseMatrixCSC 的 C++ 版本,该版本在我移植到 C++ 的项目中是特定的,而犰狳的 SpMat 未能成为完美的替代品.

template <typename Tv, typename Ti>
struct SparseMatrixCSC
{
    size_t m, n;
    arma::Col<Ti> colptr;
    arma::Col<Ti> rowval;
    arma::Col<Tv> nzval;
};

一些 Julia 函数,如 blockdiag(),允许在输入中使用类似可变参数的稀疏矩阵,在输出中使用一个 'blockdiag' 矩阵。 Julia 的脚本代码允许使用 C++17 轻松移植的一些通用方法,例如收集输入中 n 矩阵的大小,例如:

template <typename... Args>
constexpr auto blockdiag(const Args& ...args)
{
    auto mX = details::apply([](auto x) { return size(x, 1); }, args...);
    auto nX = details::apply([](auto x) { return size(x, 2); }, args...);
    auto m = sum(mX);
    auto n = sum(nX);
    ...

其中内部 details::apply 函数允许递归收集输入 n 矩阵上的 rows/cols。最终的矩阵维度在 mn 中求和。没问题,代码完美运行。

但是现在,我的问题是通过 collecting/promoting 类型 Tv(值)和 Ti(索引)来计算输出矩阵的 typename 参数类似功能的东西。根据稀疏矩阵的性质,TvTi 类型是数值。更具体地说,Ti 是一个整型。

据我所知,因为我是使用最新 C++ 标准进行元编程的真正新手,所以最好的方法是使用 <type_traits> std::common_type。但是我看不到如何解压缩可变参数模板参数(包含 SparseMatrixCSC)并使用获得一个或另一个内部列向量的 decltype arma::Col<T> 的仿函数的结果来扩展 std::common_type<...> 参数.像 :

template <typename Func, typename ... Args>
constexpr auto promote_type(Func f, Args const&... args)
{
    return typename std::common_type<(f(args), ...)>::type;
}

在之前的 blockdiag 函数中调用者:

typename Tv = details::promote_type([](auto x) { return decltype(x.nzval); }, args...);
typename Ti = details::promote_type([](auto x) { return decltype(x.rowval); }, args...);

不幸的是,对于 VS2019 16.5.4 编译器来说太糟糕了。此外,我很确定折叠表达式 (f(args), ...) 无效,不能用作模板参数。

所以,我需要你的帮助,我非常感谢你。

编辑: 为了回答巴里,promote_type 是一个 Julia 的函数,描述如下:

Promotion refers to converting values of mixed types to a single common type. promote_type represents the default promotion behavior in Julia when operators (usually mathematical) are given arguments of differing types. promote_type generally tries to return a type which can at least approximate most values of either input type without excessively widening. Some loss is tolerated; for example, promote_type(Int64, Float64) returns Float64 even though strictly, not all Int64 values can be represented exactly as Float64 values.

因为每个 args... 这里:

template <typename... Args>
constexpr auto blockdiag(const Args& ...args)

本身就是一个SparseMatrixCSC,让我们更明确一点:

template <typename... T, typename... U>
constexpr auto blockdiag(const SparseMatrixCSC<T, U>& ...args)

这既使 blockdiag 的要求更容易理解(现在我们知道它需要什么),也更容易找出答案:我们有 TU就在那里,所以只需使用'em:

template <typename... T, typename... U>
constexpr auto blockdiag(const SparseMatrixCSC<T, U>& ...args)
    -> SpareMatrixCSC<std::common_type_t<T...>, std::common_type_t<U...>>;