将模板函数传递给 std::for_each
passing template function to std::for_each
我正在尝试编写一个简单的模板函数来打印某个容器的每个元素,而不使用 for 循环。到目前为止,我有
#include <iostream>
#include <vector>
#include <algorithm>
template <typename T> void print_with_space(T x){
std::cout << x << ' ';
}
template <typename T> void print_all(T beg, T end){
std::for_each(beg, end, print_with_space<int>);
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
return 0;
}
代码编译运行,只是因为我把print_with_space<int>
放在了print_all
的实现里面。出于显而易见的原因,我只想在那里设置 print_with_space
,但是代码无法编译。我该怎么做?
您可以使用:
std::for_each(beg, end, [](const typename T::value_type& value) {
print_with_space(value);
});
T
是 std::vector<>::iterator
类型,即 RandomAccessIterator
。每个 RandomAcessIterator
都有一个基础类型,由 value_type
.
公开
因此,如果您传递 std::vector<int>::iterator
,std::vector<int>::iterator::value_type
将是 int
。
现在你有了类型,你可以创建一个 lambda,它会在每次迭代中执行。
在 C++14 中,您甚至可以这样做:
//'auto' automatically deduces the type for you
std::for_each(beg, end, [](const auto& value) {
print_with_space(value);
});
C++03 的替代方案:
#include <iterator>
template <typename T> void print_all(T beg, T end)
{
typedef typename std::iterator_traits<T>::value_type val_t;
std::for_each(beg, end, print_with_space<val_t>);
std::cout << '\n';
}
另一个选项:
template <typename T> void print_all(T beg, T end) {
std::for_each(beg, end, print_with_space<decltype(*beg)>);
std::cout << '\n';
}
适用于所有版本的 c++ 的最灵活的解决方案是使 print_with_space
成为一个函数对象。
这带来了许多优势:
- 无需在调用站点指定模板类型。
- 无需 fiddle 手动类型推导。
- 部分专业化可以通过让仿函数遵循模板化的自由函数来实现。
如:
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
// basic implementation
template<class T> void impl_print_with_space(const T& x)
{
std::cout << x << ' ';
}
// what about special handling for strings?
template<class C, class Ch, class Alloc>
void impl_print_with_space(const std::basic_string<C, Ch, Alloc>& x)
{
std::cout << std::quoted(x) << ' ';
}
// functor
struct print_with_space
{
template<class T> void operator()(const T& x) const
{
impl_print_with_space(x);
}
};
template <typename Iter> void print_all(Iter beg, Iter end)
{
std::for_each(beg, end, print_with_space());
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
auto b = std::vector<std::string> { "hello", "world" };
print_all(b.begin(), b.end());
return 0;
}
我正在尝试编写一个简单的模板函数来打印某个容器的每个元素,而不使用 for 循环。到目前为止,我有
#include <iostream>
#include <vector>
#include <algorithm>
template <typename T> void print_with_space(T x){
std::cout << x << ' ';
}
template <typename T> void print_all(T beg, T end){
std::for_each(beg, end, print_with_space<int>);
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
return 0;
}
代码编译运行,只是因为我把print_with_space<int>
放在了print_all
的实现里面。出于显而易见的原因,我只想在那里设置 print_with_space
,但是代码无法编译。我该怎么做?
您可以使用:
std::for_each(beg, end, [](const typename T::value_type& value) {
print_with_space(value);
});
T
是 std::vector<>::iterator
类型,即 RandomAccessIterator
。每个 RandomAcessIterator
都有一个基础类型,由 value_type
.
因此,如果您传递 std::vector<int>::iterator
,std::vector<int>::iterator::value_type
将是 int
。
现在你有了类型,你可以创建一个 lambda,它会在每次迭代中执行。
在 C++14 中,您甚至可以这样做:
//'auto' automatically deduces the type for you
std::for_each(beg, end, [](const auto& value) {
print_with_space(value);
});
C++03 的替代方案:
#include <iterator>
template <typename T> void print_all(T beg, T end)
{
typedef typename std::iterator_traits<T>::value_type val_t;
std::for_each(beg, end, print_with_space<val_t>);
std::cout << '\n';
}
另一个选项:
template <typename T> void print_all(T beg, T end) {
std::for_each(beg, end, print_with_space<decltype(*beg)>);
std::cout << '\n';
}
适用于所有版本的 c++ 的最灵活的解决方案是使 print_with_space
成为一个函数对象。
这带来了许多优势:
- 无需在调用站点指定模板类型。
- 无需 fiddle 手动类型推导。
- 部分专业化可以通过让仿函数遵循模板化的自由函数来实现。
如:
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
// basic implementation
template<class T> void impl_print_with_space(const T& x)
{
std::cout << x << ' ';
}
// what about special handling for strings?
template<class C, class Ch, class Alloc>
void impl_print_with_space(const std::basic_string<C, Ch, Alloc>& x)
{
std::cout << std::quoted(x) << ' ';
}
// functor
struct print_with_space
{
template<class T> void operator()(const T& x) const
{
impl_print_with_space(x);
}
};
template <typename Iter> void print_all(Iter beg, Iter end)
{
std::for_each(beg, end, print_with_space());
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
auto b = std::vector<std::string> { "hello", "world" };
print_all(b.begin(), b.end());
return 0;
}