4

我有一个程序,在对每个组件进行一些计算之后,我必须在屏幕上打印许多 STL 向量。所以我试图创建一个这样的函数:

template <typename a> 
void printWith(vector<a> foo, a func(a)){
  for_each(foo.begin(), foo.end(), [func](a x){cout << func(x) << " "; });
}

然后像这样使用它:

int main(){
  vector<int> foo(4,0);
  printWith(foo, [](int x) {return x + 1;});
  return 0;
}

printWith不幸的是,关于我在调用中放入的 lambda 表达式的类型,我遇到了编译错误:

g++ -std=gnu++0x -Wall -c vectest.cpp -o vectest.o
vectest.cpp: In function ‘int main()’:
vectest.cpp:16:41: error: no matching function for call to ‘printWith(std::vector<int>&, main()::<lambda(int)>)’
vectest.cpp:10:6: note: candidate is: void printWith()
make: *** [vectest.o] Error 1

当然,如果我这样做:

int sumOne(int x) {return x+1;}

然后printWith(foo, sumOne);按预期工作。我认为 lambda 表达式的类型将是具有推断返回类型的函数的类型。我也认为我可以在任何可以拟合正常函数的地方安装 lambda。我该如何进行这项工作?

4

4 回答 4

6

以下对我有用:

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename F>
void printWith(vector<a> foo, F f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

测试:

$ g++-4.5 -std=gnu++0x -Wall test.cpp
$ ./a.out                            
2 3 4 5 6 

或者,您可以利用没有 lambda 捕获的闭包类型可以隐式转换为函数指针的事实。这更接近您的原始代码,并且还减少了函数模板的实例化数量(在原始解决方案中,每次使用具有不同函数对象类型的函数模板时,您都会获得一个新的实例化;请注意,尽管它没有在这种特定情况下无关紧要,因为printWith函数非常短并且很可能总是内联):

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, b f(a)){
  for_each(foo.begin(), foo.end(), [=](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  printWith<int, int>(foo, [](int x) {return x + 1;});
  std::cout << '\n';
  return 0;
}

不幸的是,隐式转换不能很好地与模板参数推导配合使用:如您所见,我必须在对printWith.

另一种选择是使用std::function. 这也有助于最大限度地减少模板实例化的数量,甚至适用于具有 lambda-capture 的 lambda 表达式,但在模板参数推导方面存在相同的问题:

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

using namespace std;

template <typename a, typename b>
void printWith(const vector<a>& foo, std::function<b(a)> f){
  for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}

int main(){
  vector<int> foo = {1,2,3,4,5};
  int y = 1;
  printWith<int, int>(foo, [&](int x) { return x + y; });
  std::cout << '\n';
  return 0;
}
于 2011-06-06T16:03:27.860 回答
4

您遇到问题的原因是因为您正在尝试使用功能。自由函数具有特定的表示(作为函数指针),它不能与任何类型的函数对象互换。应该避免使用函数指针(基本上就是你在这里所拥有的)。您需要使用模板指定的类型直接获取函数对象。

template <typename a, typename Func> 
void printWith(vector<a> foo, Func func){
  for_each(foo.begin(), foo.end(), [&](a x){cout << func(x) << " "; });
}

或者,采用多态函数对象,例如std::function.

template<typename a>
void printWith(vector<a> foo, std::function<string(const a&)> func) {
    for_each(foo.begin(), foo.end(), [&](a x) { cout << func(x) << " "; });
}
于 2011-06-06T16:08:08.490 回答
2
void printWith(vector<a> foo, b func(a)){

这是错误的,你不能这样做,这会使编译器不考虑这段代码,因为它是无效的。

您有两种方法可以解决此问题:

1)不要求参数类型,只要求函子:

   void printWith(vector<a> foo, b func ){ // keep the rest of the code the same

如果 func 不采用aas 参数,则函数的其余部分将无法编译。

2)强制函子类型:

template <typename a> 
void printWith(vector<a> foo, std::function< void (a) > func ){

然后就像您使用函数指针一样。没有(或更少)编译时优化,但至少您强制执行函子签名。有关详细信息,请参阅 std::function 或 boost::function。

于 2011-06-06T15:54:12.123 回答
1

这不起作用的原因是您将模板参数推导与隐式转换混合在一起。如果你摆脱扣除它的工作原理:

printWith<int>(foo, [](int x) {return x + 1;});

但是,最好(在 printWith 内部)让我们func的 type 成为另一个模板参数,正如其他人所推荐的那样。

另一方面,如果您真的想为这种类型添加约束,有更好的方法可以使用 SFINAE(针对软错误)或static_assert(针对硬错误)来实现。

例如:

// A constraints metafunction
template<typename T, typename Element>
struct is_element_printer
    : std::is_convertible<T, Element (*)(Element)>
{};

在这里,is_element_printer<T, Element>::valueis trueiffT隐式转换为Element (*)(Element). 我只是将其用于说明目的,我不推荐将其用于实际使用:在很多不是函数指针的情况下,有很多东西可以称为“元素打印机”。我这样做只是因为std::is_convertible很容易获得,<type_traits>并且没有其他更明显的测试可用。你应该自己写。

然后:

template<typename Container, typename Functor>
void
printWith(Container&& container, Functor&& functor)
{
    // avoid repetition
    typedef typename std::decay<Container>::type::value_type value_type;

    // Check our constraints here
    static_assert(
        std::is_element_printer<
            typename std::decay<Functor>::type,
            value_type
        >::value,
        "Descriptive error message here"
    );

    // A range-for is possible instead
    std::for_each(container.cbegin(), container.cend(), [&functor](value_type const& v)
    { std::cout << functor(v) << ' '; });
}
于 2011-06-06T17:19:33.093 回答