总结std :: integer_sequence中的元素

时间:2019-03-19 17:37:24

标签: c++ visual-studio-2017 variadic-templates template-meta-programming

出于教育目的,我尝试创建一个std::integer_sequence并将其元素总结为参数包。我希望这很简单,并在下面编写了代码。

第1步:创建了add()操作集,以正确处理基于整数的均质参数包。经过add<0,1,2,3>()通话测试,并且可以正常工作。

步骤2:在这一步中,我想传递一个std::integer_sequence作为模板参数,以求和序列中的整数。我失败了,请参阅注释掉的代码。

如何传递std::integer_sequence作为模板参数?

一个错误消息是没有功能模板总数的实例与调用匹配。为什么?我期望整数序列等效于int参数包。

另一则错误消息表明Args模板参数不是恒定的。怎么可能我希望这是一个后续错误,但是我在实验中经常看到这种错误组合。

我正在使用Microsoft VC ++ 2017编译器。

#include <iostream>
#include <utility>

template <typename T>
T add(T first) {
    return first;
}

template <typename T, typename ... Args>
T add(T car, Args... cdr) {
    return car + add(cdr...);
}

template <int ... Args>
int total() {
    return add(Args...);
}


int main(int argc, char** argv)
{
    using std::cout;
    using std::endl;

    int s1 = 
        total<0, 1, 2, 3>();
    std::cout << "s1 = " << s1 << std::endl;

    // The following does not compile:
#if 0
    int s2 = 
        total<std::make_integer_sequence<int, 4>>();
    std::cout << "s2 = " << s2 << std::endl;

    static_assert(s1 == s2, "no match");
#endif

    // This should work:
    //
    {
        cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
        cout << std::make_integer_sequence<int, 4>().size << endl;;
    }

    return 0;
}

...后来,经过很多有用的评论。我学到了很多东西,现在有了以下工作代码:

#include <iostream>
#include <utility>

template <typename T>
constexpr T add(T first) {
    return first;
}

template <typename T, typename ... Args>
constexpr T add(T car, Args... cdr) {
    return car + add(cdr...);
}

template <int ... Args>
constexpr int total() {
    return add(Args...);
}

template<int... Args>
constexpr int total(std::integer_sequence<int, Args...>) {
    return total<Args...>();
}

int main(int argc, char** argv)
{
    using std::cout;
    using std::endl;

    constexpr int s1 =
        total<0, 1, 2, 3>();
    std::cout << "s1 = " << s1 << std::endl;

    // The following now compiles. Oh, happy day!
#if 1
    constexpr int s2 =
        total(std::make_integer_sequence<int, 4> {});

    std::cout << "s2 = " << s2 << std::endl;

    static_assert(s1 == s2, "no match");
#endif

    // This should work:
    //
    {
        cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
        cout << std::make_integer_sequence<int, 4>().size << endl;;
    }

    return 0;
}

4 个答案:

答案 0 :(得分:4)

使用fold expressions会更容易:

template<class T, T... Args>
constexpr T total(std::integer_sequence<T, Args...> = {}) {
    return (Args + ...);
}

int main() {
    int s2 = 
        total(std::make_integer_sequence<int, 4>{});
    // Or calling directly
    int s1 = total<int, 0, 1, 2, 3>();
}

请注意默认的std::integer_sequence参数,因此,如果传递整数序列,它将推导Args参数。

如果您希望能够使用模板中的std::integer_sequence类型进行调用,只需创建一个辅助函数即可:

template<class IntegerSequence>
constexpr auto total_of(IntegerSequence argument = {}) {
    return total(argument);  // Will deduce `T` and `Args`
}

total_of<std::make_integer_sequence<int, 4>>();

答案 1 :(得分:1)

  

我希望整数序列等效于一个int参数包。

不是:std::integer_sequence是一种类型;所以

total<std::make_integer_sequence<int, 4>>();

可以匹配接收类型的模板total()函数

template <typename T>
int total ()
 { return ???; }

问题在于,从类型T中,您不容易提取整数序列。

  

如何传递std :: integer_sequence作为模板参数?

解决方案(可能是可能的解决方案)是局部专业化。

不幸的是,一个函数不能是部分专用的。

但是如果totalstruct(或class),则全部不同。

您可以按如下方式编写total

template <typename>
struct total;

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

所以您可以通过以下方式使用它

constexpr auto s2 { total<std::make_integer_sequence<int, 4>>::value };

主题外:声明constexpr要在static_assert()测试中使用的变量。

以下是完整的C ++ 17示例(为避免与total函数发生冲突,将total2结构重命名为total()

#include <utility>

template <typename>
struct total2;

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

template <typename T>
constexpr T add (T first)
 { return first;}

template <typename T, typename ... Args>
constexpr T add (T car, Args ... cdr)
 { return car + add(cdr...); }

template <int ... Args>
constexpr int total ()
 { return add(Args...); }

int main ()
 {
   constexpr auto s1 { total<0, 1, 2, 3>() };
   constexpr auto s2 { total2<std::make_integer_sequence<int, 4>>::value };

   static_assert(s1 == s2);
 }

答案 2 :(得分:0)

执行此操作的一种方法是为total添加重载并以不同的方式调用它:

template<int... Args>
int total(std::integer_sequence<int, Args...> ) {
    return add(Args...);
}

比打电话

nt s2 = 
    total(std::make_integer_sequence<int, 4>>{});

这将解决呼叫问题。为了使用static_assert中函数的结果,您还需要确保将其(以及存储结果的变量)标记为constexpr

答案 3 :(得分:0)

  

我希望整数序列等效于一个int参数包。

但是您可以通过写出以下两个扩展来看到它:

removeTagList

第一种情况是

from py2cytoscape import cyrest
cytoscape=cyrest.cyclient()
cytoscape.status()

这是一个有效的整数包,第二个是

template <int ... Args> int total();

这显然不像整数包。链接到std::integer_sequence的文档以供参考。

如果要将整数序列转换回传递给total<0, 1, 2, 3>(); // => Args... is (0, 1, 2, 3) 的原始参数包,则需要使用模板参数推导重载SergeyA的答案显示:

total<std::make_integer_sequence<int, 4>>();
// => Args... must be std::integer_sequence<int, 0, 1, 2, 3>

(函数参数是一个虚拟变量,仅用于推导make_integer_sequence

不过,如果可以使用fold表达式,它比老式的递归要好得多。