这个模板函数的最后一个参数有什么用?

时间:2017-03-22 11:16:10

标签: c++ templates

在一本关于C ++的书中,我找到了以下代码:

#include <iostream>
using namespace std;

template<class T> T sum(T* b, T* e, T init = T()) {
    while(b != e)
    init += *b++;
    return init;
}

int main() {
    int a[] = { 1, 2, 3 };
    cout << sum(a, a + sizeof a / sizeof a[0]) << endl; // 6
}

我的模板函数T init = T()的最后一个参数有什么用? 作者说以下关于第三个论点:

  

sum()的第三个参数是累积的初始值   元素。

但这怎么可行呢?拥有3个参数但只使用前两个参数不是一个坏习惯吗?这适用于所有类型或换句话说,是否在所有数据类型的C ++标准中定义了T()

注意:我从测试中知道它将init初始化为0

6 个答案:

答案 0 :(得分:3)

最后一个论点是初始值,我说。这个想法可能是你可以继续求和,或者不清楚零元素是什么。

这样的参数是所谓的默认参数,很常见。例如,如果您制作了自己的矢量类:

template <class T>
Vector(const unsigned int size, const T& default_value = (T) 0);

然后,您可以通过一个参数或两个Vector<double> vector(2);Vector<double> vector(2, 5.);创建一个Vector对象。第一次调用产生一个大小为2的向量,该向量全为零,第二次调用大小为2的向量,所有条目为5。

在您的示例中,您可以为总和添加内容。

在您的情况下,调用标准构造函数,可能假设它是零元素。

那就是说,如果你的书倾向于使用指针算术,并且习惯先省略{} -brackets,然后甚至不使用缩进而忽略它们,我推荐另一本书。

答案 1 :(得分:2)

它被称为vue-loader,它给它一个默认值。

粗略地说,对于值初始化,如果T是内置类型,那么它是零初始化的,如果它是类类型,那么将调用默认构造函数。

第三个参数用于此类用法:

cout << sum(a, std::end(a), 12) << endl; // 18

答案 2 :(得分:2)

  

但这怎么可行呢?

正如其他人所说,第三个参数将是默认构造的,或者在原语(如int)的情况下,它将调用自由浮动函数,该函数将创建,初始化并返回一个值0

  

拥有3个参数但只使用前两个参数是不是一个坏习惯?

不一定。我的意思是,大多数语言都存在默认参数。它有助于减少打字量和心理开销。此外,有时开发人员确实知道一个好的&#34;参数的默认值。在求和的情况下,通常人们想要从0开始,并且通常在基元上调用求和。

  

这适用于所有类型,换句话说,是否在所有数据类型的C ++标准中定义了T()?

不,不是。您可以创建必须使用某些参数构造的任意类型。在这种情况下,您不得不自己提供第三个参数。例如:

struct IntHolder
{
    int value = 0;
    IntHolder(int _init) : value(_init){}
    IntHolder& operator+=(const IntHolder& other)
    {
        value += other.value;
        return *this;
    }
};

template<class T> T sum(T* b, T* e, T init = T()) {
    while(b != e)
    init += *b++;
    return init;
}

int main() {
    std::vector<IntHolder> a{1,2,3};
    cout << sum(a.data(), a.data()+a.size(), IntHolder(0)).value << endl; // 6
}

答案 3 :(得分:1)

以这种方式指定泛型参数的一个非常有用的功能是,相同的代码可以应用于非数字类型,前提是它们支持默认构造函数和operator+=

#include <iostream>
#include <string>

template<class T> T sum(T* b, T* e, T init = T()) {
    while(b != e)
        init += *b++;
    return init;
}

int main() {
    std::string a[] = { "the ", "cat ", "sat ", "on ", "the ", "mat" };
    std::cout << sum(a, a + sizeof a / sizeof a[0]) << std::endl;
}

但是,作者应该在迭代器方面实现泛型函数,因为它的限制性较小:

#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <array>

//
// better - now works with all forward iterator models, including pointers
//
template<class Iter, class T = typename std::iterator_traits<Iter>::value_type>
T sum(Iter b, Iter e, T init = T()) {
    while(b != e)
        init += *b++;
    return init;
}

int main() {
    std::string a[] = { "the ", "cat ", "sat ", "on ", "the ", "mat" };
    std::cout << sum(a, a + sizeof a / sizeof a[0]) << std::endl;
    std::cout << sum(std::begin(a), std::end(a)) << std::endl;

    std::vector<int> vi = { 1, 2, 3, 4, 5, 6 };
    std::cout << sum(std::begin(vi), std::end(vi)) << std::endl;

    std::array<int, 6> ai = { 1, 2, 3, 4, 5, 6 };
    std::cout << sum(std::begin(ai), std::end(ai)) << std::endl;
}

答案 4 :(得分:0)

使用第三个参数,您可以指定累积的初始量。试试这个:

cout << sum(a, a + sizeof a / sizeof a[0], 10); //16

答案 5 :(得分:0)

你如何写一个(微不足道的)整数数组?可能是这样的:

int sum = 0;
for (int i=0; i<length; i++) sum = sum+t[i];

如果您有T个数组(包含适当的运算符+=),则更“通用”:

T sum = T();
for (int i=0; i<l; i++) sum = sum+t[i];

当然,您需要的(稍微更通用)是为sum提供您想要的任何第一(初始)值。这就是为什么第三个参数存在默认值,让您忽略它。

并非所有类都提供了一个没有参数的ctor可调用,但是在C ++非正式编程规则中存在 canonical 类的概念,你必须提供一个没有参数的ctor可调用,因此默认值。