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

What is the use of the last argument on this template function?

本文关键字:参数 什么 最后一个 函数      更新时间:2023-10-16

在一本关于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 个参数但只使用其中的前两个不是坏习惯吗?这是否适用于所有类型,或者换句话说,T()在所有数据类型的C++标准中定义?

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

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

像这样的参数是所谓的默认参数,很常见。例如,如果您创建了自己的向量类:

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。

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

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

也就是说,如果你的书倾向于使用指针算术,并且习惯于先省略{}括号,然后在省略它们的地方甚至不使用缩进,我会推荐另一本书。

它被称为值初始化,它给了它一个默认值。

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

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

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

但是这怎么能工作呢?

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

有 3 个参数但只使用其中的前两个不是坏习惯吗?

不一定。我的意思是,默认参数存在于大多数语言中。它有助于减少打字量和精神开销。此外,有时开发人员确实知道参数的"好"默认值。在求和的情况下,通常人们希望从0开始,通常求和被称为基元。

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

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

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
}

以这种方式指定泛型参数的一个非常有用的功能是,相同的代码可以应用于非数字类型,前提是它们支持默认构造函数并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;
}

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

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

您将如何编写整数数组的(微不足道的)总和?大概是这样的:

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++非正式编程规则中存在规范类的概念,为此您必须提供不带参数的 ctor 可调用对象,因此为默认值。

相关文章: