2009-12-18 34 views
43

我有一个关于哪种风格是首选的问题:std :: bind Vs lambda in C++ 0x。我知道他们服务于某些目的 - 不同的目的,但让我们举一个相交功能的例子。绑定与Lambda?

使用lambda

uniform_int<> distribution(1, 6); 
mt19937 engine; 
// lambda style 
auto dice = [&]() { return distribution(engine); }; 

使用bind

uniform_int<> distribution(1, 6); 
mt19937 engine; 
// bind style 
auto dice = bind(distribution, engine); 

哪一个我们应该更喜欢哪个?为什么?假设比上述例子更复杂的情况。即彼此的优点/缺点是什么?

+1

是否有任何性能差异?速度,内存消耗,堆使用率? – 2009-12-19 03:33:44

+0

@Caspin我不知道这两个工具之间的性能/内存消耗是否有差异:) – AraK 2009-12-19 04:54:38

+4

btw:这两个版本并不相同,因为绑定复制参数。替代方法:bind(ref(distribution),ref(engine)) – sellibitze 2009-12-19 10:32:08

回答

22

正如你所说,绑定和lambda不完全针对相同的目标。例如,对于使用和组合STL算法,lambda是清晰的赢家,恕我直言。为了说明这一点,我记得有一个非常有趣的答案,在堆栈溢出问题上,有人提出了十六进制幻数的想法(如0xDEADBEEF,0xCAFEBABE,0xDEADDEAD等),并被告知如果他是一个真正的C++程序员他也只会下载的英语单词列表,并使用C++一个简单的一行:)

#include <iterator> 
#include <string> 
#include <algorithm> 
#include <iostream> 
#include <fstream> 
#include <boost/lambda/lambda.hpp> 
#include <boost/lambda/bind.hpp> 

int main() 
{ 
    using namespace boost::lambda; 
    std::ifstream ifs("wordsEn.txt"); 
    std::remove_copy_if(
     std::istream_iterator<std::string>(ifs), 
     std::istream_iterator<std::string>(), 
     std::ostream_iterator<std::string>(std::cout, "\n"), 
     bind(&std::string::size, _1) != 8u 
      || 
     bind(
      static_cast<std::string::size_type (std::string::*)(const char*, std::string::size_type) const>(
       &std::string::find_first_not_of 
      ), 
      _1, 
      "abcdef", 
      0u 
     ) != std::string::npos 
    ); 
} 

这个片段中,在纯C++ 98,打开英语单词文件,扫描每个字并打印'a','b','c','d','e'或'f'字母的长度为8的字母。

现在,打开的C++ 0x和lambda:

#include <iterator> 
#include <string> 
#include <algorithm> 
#include <iostream> 
#include <fstream> 

int main() 
{ 
std::ifstream ifs("wordsEn.txt"); 
std::copy_if(
    std::istream_iterator<std::string>(ifs), 
    std::istream_iterator<std::string>(), 
    std::ostream_iterator<std::string>(std::cout, "\n"), 
    [](const std::string& s) 
    { 
     return (s.size() == 8 && 
       s.find_first_not_of("abcdef") == std::string::npos); 
    } 
); 
} 

这仍然是一个有点重(这主要是因为istream_iterator业务)来读取,但比绑定版本简单得多:)

+0

尽管这两段代码不会做同样的事情,但我的观点非常清楚:) – AraK 2009-12-19 04:56:38

+4

lambda应该是:[](const std :: string&s) - > bool – 2009-12-19 11:00:53

+9

@Beh Tou Cheh I认为如果lambda包含'return ;'only(如Thomas所做的那样),则应该推断出该类型。 – AraK 2009-12-19 11:09:48

17

C++ 0x lamdba语法比绑定语法更具可读性。一旦进入超过2-3级的绑定,你的代码就变得难以理解,难以维护。我更喜欢更直观的lambda语法。

+0

不同意。 '[this](){Type * this_too = this; run([this_too](){this_too-> f();});}'既不可读也不直观。 – 2015-02-25 12:55:28

+5

无可否认,我认为新的行会帮助你的反例。新行对绑定没有多大帮助。 – 2016-07-21 18:08:19

3

我认为这更多的是品味的问题。快速掌握新技术或熟悉函数式编程的人可能更喜欢使用lambda语法,而更保守的程序员肯定会更喜欢绑定,因为它更像传统的C++语法。

这样的决定应该与那些将与代码一起工作的人协调,可能是通过多数票。

但是,这并没有改变这个事实,即lambda语法更加强大和更清晰。

+3

团队中的人不断变化。代码可读性尤其重要。为未来的维护程序员。因此,我们应该选择提供更多可读性的解决方案,在lamdba和bind之间,lamda肯定会拿到蛋糕。 – posharma 2009-12-18 22:25:24

8

lambda表达式的好处之一是,当你需要在现有函数之上添加一些逻辑时,它们会更有用。

使用绑定,即使逻辑在这一个地方只被需要,你也不得不创建一个新的函数/方法/仿函数。你需要提出一个合适的名称,并且可能会使代码变得不那么容易理解,因为它可能会让你分解相关的逻辑。

使用lambda,您可以在lambda内添加新逻辑(但如果创建新的可调用函数有意义,则不会强制执行)。

+1

+1。我不得不在dtor中关闭一个FILE *向量。而不是能够使用lambda'[](FILE * f){if(f)fclose(f); }'我必须创建一个命名函数并使用它。该函数出现在类的'private'部分,因此被'for_each'调用的多行分隔 – KitsuneYMG 2009-12-19 09:15:39

0

C++ 0x lambdas基本上替换绑定。没有什么可绑定的,你不能重新创建一个简单的包装lambda来实现相同。一旦lambda支持广泛传播,std :: tr1 :: bind就会成为std :: bind1st等等。这很好,因为大多数程序员因为某种原因而难以获得绑定的头脑。

+3

虽然这个答案在发布时不正确,但它对于C++ 14来说是准确的。上面评论中的链接现在证实了这一点。 – 2016-05-24 04:29:53

+0

@gnzlbg请参阅上面的评论。 – 2017-01-17 15:11:54

39

C++ 0x lambdas是单形的,而bind可以是多态的。你不能有像

auto f = [](auto a, auto b) { cout << a << ' ' << b; } 
f("test", 1.2f); 

a和b必须有已知的类型。在另一方面,TR1 /升压/凤凰/λ结合可以让你做到这一点:

struct foo 
{ 
    typedef void result_type; 

    template < typename A, typename B > 
    void operator()(A a, B b) 
    { 
    cout << a << ' ' << b; 
    } 
}; 

auto f = bind(foo(), _1, _2); 
f("test", 1.2f); // will print "test 1.2" 

注意的是,A型和B 固定在这里。只有当f被实际使用时,这两个才会被推断出来。

+0

为什么不直接用显式类型参数声明lambda?这将比上面所示的绑定解决方案有很大的改进。另外,如果你想要复用更复杂的功能,lambda仍然比bind更好,因为它不需要结构,即使你想将状态绑定到仿函数中:'template <...> foo(A a,B b,int state){cout ... << state; } ... auto f = [](const char * a,float b){foo(a,b,42); };'。 – 2011-04-07 03:17:09

+2

@Marcelo Cantos:要证明的陈述是“C++ 0x lambdas是单形的”,正是因为你必须用显式类型的参数声明lambda表达式。 – MSalters 2011-04-07 09:15:23

+0

@ MSalters:问题是(大致):“哪个更好?”我不确定如何证明C++ 0x lambda是单形的回答这个问题。 – 2011-04-07 12:17:25