我想知道是否有一种方法可以创建一个模板函数,该函数引用特定类型的任何集合。即类似:
class Bob
{
public:
int age;
int height;
}
template<class T>
void functionWhichIteratesOverBobs(T &bobs)
{
int totalAge = 0;
for(auto &bob: bobs)
{
totalAge += bob.age;
}
}
基本上有没有一种方法可以在模板函数的定义中要求 T 有一个 begin() 和 end() 函数,它们返回一个迭代器给 T。
我已经看到了以下问题,但这需要一个带有开始和结束的函数,即
std::vector<Bob> bobs;
functionWhichIteratesOverBobs(bob.begin(), bob.end());
当我想要的是:
std::vector<Bob> bobs;
functionWhichIteratesOverBobs(bobs);
如果您希望保持过载集不受污染,请使用表达式 SFINAE,如下所示:
template<class T>
void functionWhichIteratesOverBobs(T &bobs)
-> decltype(std::begin(bobs), std::end(bobs), void()) {
// [..Range based for over bobs..]
}
请记住,如果给出了错误的参数,您显示的函数模板将不会在没有错误的情况下实例化,因此这目前是多余的 - 直到您开始重载
functionWhichIteratesOverBobs
。
C++ 概念 Container 有一个成员类型
value_type
所包含元素的类型,因此您可以像使用迭代器一样使用 SFINAE - 甚至更简单,因为您不需要使用 iterator_traits
:
template<class T>
auto functionWhichIteratesOverBobs(T &bobs)
-> std::enable_if_t<std::is_same_v<typename T::value_type, Bob>>
{
// ...
}
请注意,这不适用于原始数组;如果你想让它们起作用,你可以使用
decltype(std::begin(bobs))
和 iterator_traits
:
template<class T>
auto functionWhichIteratesOverBobs(T &bobs) -> std::enable_if_t<std::is_same_v<
typename std::iterator_traits<decltype(std::begin(bobs))>::value_type, Bob>>
{
// ...
}
在 C++ 20 中,您可以使用范围概念:
#include <ranges>
template<std::ranges::range T>
void functionWhichIteratesOverBobs(T &bobs)
这将检查 T 是否是一个范围,即有开始和结束。
范围库中有更细粒度的概念。
https://en.cppreference.com/w/cpp/ranges#Range_concepts
我发现这比 SFINAE 更具可读性