【问题标题】:How to write generic version of variadic template class that calculates sum of integers如何编写计算整数和的可变参数模板类的通用版本
【发布时间】:2019-05-23 19:15:34
【问题描述】:

我在这个网站上发现了以下代码(如下)

https://www.modernescpp.com/index.php/c-insights-variadic-templates

但此声明/定义仅适用于整数,我想编写一个适用于其他类型的版本,如 float、double、std::strings 和重载“+”运算符的用户定义类型。但是我正在努力写一个。

请注意,上述网站有基于可变参数函数模板的解决方案,适用于不同的数据类型,包括浮点类型(尚未尝试使用用户定义的类型)。我正在寻找基于可变参数模板类的解决方案。这纯粹是为了学习目的。

有人可以帮我解决这个问题吗?

#include<iostream>

template<int...>
struct add;

template<>
struct add<>
{
  static constexpr int value = 0;
};

template<int i, int... tail>
struct add<i, tail...>
{
  static constexpr int value = i + add<tail...>::value;
};

int main()
{
    std::cout << add<1,2,3,4>::value;
}

我写了这个但是卡住了

template<typename ...>
struct add;

template<typename T, typename... args>
struct add<T, args...>
{
    static constexpr T value = ??//T();//what to write here?
};

提前致谢。

【问题讨论】:

  • 以原始示例并将int 替换为auto。您可以使用折叠表达式简化代码。
  • C++11、C++14 还是 C++17?
  • @NeutronStar 一票赞成这个好问题。我发布了一个答案。请看一下,如果需要进一步解释,请随时询问。我也感谢投票/接受!

标签: c++ variadic-templates template-meta-programming


【解决方案1】:

下面的东西呢?

#include <iostream>

template <typename T, T...>
struct add
{ static constexpr T value = 0; };

template <typename T, T head, T ... tail>
struct add<T, head, tail...>
{ static constexpr T value = head + add<T, tail...>::value; };

int main()
 {
    std::cout << add<int, 1, 2, 3, 4>::value << std::endl;
    std::cout << add<long, 10l, 20l, 30l, 40l>::value << std::endl;
 }

或者,也许更好,继承自 std::integral_constant

template <typename T, T...>
struct add : public std::integral_constant<T, T{0}>
{ };

template <typename T, T head, T ... tail>
struct add<T, head, tail...>
   : public std::integral_constant<T, head + add<T, tail...>::value>
{ };

如果可以使用 C++17,则不再需要递归,但可以使用模板折叠。

template <typename T, T... Is>
struct add : public std::integral_constant<T, (... + Is)>
 { };

C++17 还为您提供摆脱 typename T 类型参数的机会,使用 auto 作为值。

问题变成了:考虑到模板值可能属于不同类型,value 是哪种类型?

我想std::common_type 可以解决这个问题,所以

#include <iostream>
#include <type_traits>

template <auto ... Is>
struct add :
   public std::integral_constant<std::common_type_t<decltype(Is)...>,
                                 (... + Is)>
 { };

int main()
 {
    std::cout << add<1, 2, 3, 4>::value << std::endl;
    std::cout << add<10l, 20l, 30l, 40l>::value << std::endl;
 }

或者,也许,只是使用decltype((... + Is))

template <auto ... Is>
struct add :
   public std::integral_constant<decltype((... + Is)), (... + Is)>
 { };

题外话:原add可以简化如下

template <int...>
struct add
 { static constexpr int value = 0; };

template <int i, int... tail>
struct add<i, tail...>
 { static constexpr int value = i + add<tail...>::value; };

我的意思是:不是两个特化,而是一个主版本(即递归的基本情况)和一个特化(递归情况)。

或者,至少,我认为这有点简单。

【讨论】:

  • 感谢您向我展示了各种方法。但我希望它适用于浮点类型、字符串和用户定义类型。我尝试使用 double 和 std::string 并得到错误“非类型模板参数不能是 double 类型或 basic_string
  • @NeutronStar - 抱歉:我不明白问题在于将类型用作浮动或类。不幸的是,浮点值(还有std::string 值)不能作为模板参数。我能想象的最好的方法是在某种类型的结构中插入一个浮点作为static constexpr 值,并将该结构(类型)作为模板参数传递。但是,正如您可以想象的那样,这是一种完全不同类型的解决方案,并且没有那么优雅。
【解决方案2】:

可能非整数类型进行一些修改!

模板非类型,非模板参数应该是整数类型或具有链接或一些有限的更多可能性的引用/指针。可以在Template parameters and template arguments 阅读完整列表。

由于浮动类型不能以模板非类型、非模板参数/参数的形式出现,最好的下一个选择是通过引用来获取它们。

所以struct 变成这样:

template<auto& ...>
struct add{
    static constexpr auto value = 0;
};
template<auto& first, auto& ... others>
struct add<first, others...>{
    static constexpr auto value = first + add<others ...>::value;
};

值应该首先存储为常量(带有链接),所以在main()之前:

const auto v1 = 12; //int
const auto v2 = 54L; //long
const auto v3 = 3.25242; //double
const auto v4 = 75.7256L; //long double

然后它们可以在任何地方使用:

#include <iostream>
int main(){
    std::cout << add<v1, v2, v3, v4>::value << std::endl;
}

可能的输出:

144.978

它不仅适用于(混合)整数类型和(混合)浮点类型,还适用于任何自定义类型,前提是自定义类型满足特定属性,包括具有constexpr 构造函数和operator +。它还必须具有某种类型的转换运算符或其他方式来实现类似的功能。 例如可以使用这种类型:

class custom_type{
    const float v;
    //this one works too but the first is better for the purpose.
    //float v;
public:
    template<typename T>
    constexpr custom_type(T v_):v(v_){}
    template<typename T>
    constexpr auto operator +(T o)const{
        return o + 7345 + v ;
    }
    //this one works but the next one is  better for the purpose.
    //operator auto()const{
    //this one works too but the next one is more clear.
    //constexpr operator auto()const{
    template<typename T>
    constexpr operator T()const{
        return v;
    }
};

把它们放在一起:

template<auto& ...>
struct add{
    static constexpr auto value = 0;
};
template<auto& first, auto& ... others>
struct add<first, others...>{
    static constexpr auto value = first + add<others ...>::value;
};

class custom_type{
    const float v;
public:
    template<typename T>
    constexpr custom_type(T v_):v(v_){}
    template<typename T>
    constexpr auto operator +(T o)const{
        return o + 7345 + v ;
    }
    template<typename T>
    constexpr operator T()const{
        return v;
    }
};

const auto v1 = 12; //int
const auto v2 = 54L; //long
const auto v3 = 3.25242; //double
const auto v4 = 75.7256L; //long double
const custom_type v5 = 34.234; //custom_type

#include <iostream>
int main(){
    std::cout << add<v1, v2, v3, v4, v5>::value << std::endl;
}

可能的输出:

7524.21

请注意,低于 17 的 C++ 版本的 struct add 只能接受单一类型的参数,而 double 类型的参数如下:

template<const double& ...>
struct add{
    static constexpr double value = 0;
};
template<const double& first, const double& ... others>
struct add<first, others...>{
    static constexpr double value = first + add<others ...>::value;
};

还有常量:

const double v1 = 12;
const double v2 = 54L;
const double v3 = 3.25242;
const double v4 = 75.7256l;

祝你好运!

【讨论】:

  • 非常感谢您的详细解答。我只是在寻找这个东西。我在这里真的学到了一些新东西,特别是如何通过 const 引用传递非类型模板参数。还要感谢您展示自定义类型的示例。在不知道我需要一个 constexpr 构造函数的情况下,我肯定会为它而苦苦挣扎。让我更多地使用您的代码,如果我有更多疑问,我会回复您。
  • @NeutronStar (正如我之前所说)谢谢你的问题朋友!让我告诉你,它还为我提出了一些问题(关于Storage duration and linkage),我想我会问自己!所以再次感谢您的问题,您的投票并接受我的回答!
【解决方案3】:

我假设您出于某种原因不想编写简单的折叠表达式。

我们需要一个T 类型的实际 作为(非类型)模板参数。最简单的方法是使用 auto 作为它的类型:

template<auto ...>
struct add;

template<auto t>
struct add<t>
{
    static constexpr auto value = t;
};

template<auto t, auto... args>
struct add<t, args...>
{
    static constexpr auto value = t + add<args...>::value;
};

演示:

#include <iostream>
#include <string>
int main()
{
    std::cout << add<1, 2, 3>::value << '\n';
    std::cout << add<1u, 2, -4>::value << '\n';
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-25
    • 1970-01-01
    • 2010-12-22
    • 2016-12-11
    • 2021-10-01
    • 1970-01-01
    相关资源
    最近更新 更多