此模板函数上的最后一个参数有什么用?
What is the use of the last argument on this template function?
在一本关于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 可调用对象,因此为默认值。
- 传递给std::function template的template参数究竟代表什么
- 什么..(省略号)作为函数原型中唯一的函数参数,C++?
- C++部分概念 id:显式模板规范顺序/第一个参数的特殊状态的原因是什么?
- 引用捕获和在 lambda 中通过引用发送参数有什么区别 (C++)
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- 读取大文件(>2GB)(文本文件包含以太网数据)并通过不同参数随机访问数据的最佳方法是什么?
- 未命名的非类型模板参数有什么意义?
- 有什么方法可以将具有不同模板参数的模板类实例放入向量中?
- 什么是仅调用一次并调用参数的控制台应用
- 在模板参数中使用 {} 在 type_trait{} 中时,其作用是什么<T>?
- 在 C 和 C++ 中作为函数参数,int **a 和 int a[][] 之间有什么确切的区别
- 为什么或在什么情况下,你会将参数作为C++中的引用(或指针)传递给函数?
- 没有大小参数的CString::GetBuffer()做什么
- 可变参数模板和省略号有什么区别?
- 当通知迭代器参数初始化为空列表的开头时,list::insert 行为是什么?
- C++ [错误] 声明'char '隐藏参数。什么意思?
- 用相同的参数声明两个构造函数的最偶像化的方法是什么?
- 使用作为参数返回的指针的最佳做法是什么
- C++构造函数调用 boost::文件系统::p ath 作为参数 - 什么也没发生
- 如果我只想指定哈希函数,我应该传递给unordered_map的存储桶计数参数什么?