【问题标题】:Does bind() have any advantage (other than compatibility) over C++11 lambdas?与 C++11 lambda 相比,bind() 是否有任何优势(除了兼容性)?
【发布时间】:2012-08-21 20:38:43
【问题描述】:

我正在考虑将我的代码迁移到使用 C++11 风格的 lambda,而不是到处使用 binds。但我不确定这是否是个好主意。

是否使用例如boost::lambda(或boost::phoenix)比 C++11 风格的 lambda 有什么实际优势吗?

改用 lambdas 是个好主意吗?我应该迁移我的代码吗?

【问题讨论】:

  • 有时 boost lambda 的东西写起来要少得多,有时你不必重复类型......
  • @PlasmaHH:是的。可读性、兼容性和多态性是 SO 上很多地方提到的优势,当人们询问 lambda 的优势(如 here)或尝试比较 lambda 和 bind 时。但是我发布了这个问题(和答案)以指出 即使您处于这种情况它们可读和可用(即即使您不需要多态性,即使可读性不是问题),仍然有理由选择bind
  • 我不知道 boost 的 lambda。但在 C++11 的上下文中,我从 Scott Meyers 的演讲中听到:“Lambda 通常更可取,它们通常会生成更好的代码。通过绑定调用涉及函数指针⇒ 没有内联。通过闭包调用允许完全内联。”

标签: c++ lambda c++11 boost-lambda boost-phoenix


【解决方案1】:

主要优点是多态函子。目前,C++11 lambda 是单态的,即它们只接受单个参数类型,而 bind() 允许您创建接受任何参数类型的函子,只要绑定的函子可以被它调用。

#include <functional>

struct X{
  template<class T, class U>
  void operator()(T, U) const{}
};

int main(){
  X x;
  auto l_with_5 = [x](int v){ return x(v, 5); };
  auto b_with_5 = std::bind(x, std::placeholders::_1, 5);
  l(4);
  b("hi"); // can't do that with C++11 lambdas
}

【讨论】:

  • 哈哈,我问这个问题的前提是首先使用 C++11 lambda 或 boost::lambda 一样容易,但这是一个很好的观点,+1。
  • 我认为“绑定是多态的”应该被接受,但问题是,它已经在 StackOverflow 上的一百万个其他地方提到,例如 here。另一方面,我没有看到 SO 上其他地方提到的代码大小问题,所以既然人们可能会发现这个问题,我想我可能会接受我自己的答案,如果只是为了把它带给人们也请注意,尽管总体而言这是一个更好的答案...
【解决方案2】:

是的,Boost lambdas 是多态的,C++11 lambdas 不是。这意味着,例如,您不能使用 C++11 lambdas 来做到这一点:

template<class T>
void f(T g)
{
    int x = 123;
    const char* y = "hello";
    g(x); // call with an integer
    g(y); // call with a string
}

int main() {
    f(std::cout << _1);
}

【讨论】:

  • 另见this;它也适用于您的答案。
【解决方案3】:

是的:它可以(有时)显着影响输出大小。

如果您的 lambda 在任何方面彼此不同,它们将生成不同的代码,编译器可能无法合并相同的部分。 (内联使这变得更加困难。)

当您第一次看到它时,这看起来没什么大不了的,直到您注意到:
当您在 std::sort 等模板化函数中使用它们时,编译器会为每个不同的 lambda 生成新代码

这会不成比例地增加代码大小。

然而,bind 通常更能适应此类变化(尽管不能免疫它们)。

为了说明我的意思...

  1. 以下面的例子为例,用 GCC(或 Visual C++)编译它,并记下输出的二进制大小。
  2. 尝试将if (false) 更改为if (true),并查看输出二进制大小的变化情况。
  3. 在注释掉除一个每个部分中的stable_sorts 之外的所有内容后重复#1 和#2。

注意,第一次时,C++11 lambda 略小;在那之后,它们的大小在每次使用后都会爆炸(每次使用 VC++ 大约需要 3.3 KB 的代码,与 GCC 类似),而基于boost::lambda 的二进制文件几乎没有改变它们的大小(当所有四个都包括在内,到最接近的半千字节)。

#include <algorithm>
#include <string>
#include <vector>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>   // can also use boost::phoenix

using namespace boost::lambda;

struct Foo { std::string w, x, y, z; };

int main()
{
    std::vector<Foo> v1;
    std::vector<size_t> v2;
    for (size_t j = 0; j < 5; j++) { v1.push_back(Foo()); }
    for (size_t j = 0; j < v1.size(); j++) { v2.push_back(j); }
    if (true)
    {
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::w, var(v1)[_1]) < bind(&Foo::w, var(v1)[_2]));
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::x, var(v1)[_1]) < bind(&Foo::x, var(v1)[_2]));
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::y, var(v1)[_1]) < bind(&Foo::y, var(v1)[_2]));
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::z, var(v1)[_1]) < bind(&Foo::z, var(v1)[_2]));
    }
    else
    {
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].w < v1[j].w; });
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].x < v1[j].x; });
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].y < v1[j].y; });
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].z < v1[j].z; });
    }
}

请注意,这是“以规模换速度”;如果您处于非常非常紧密的循环中,它可能涉及一个额外的变量(因为现在它使用指向成员的指针)。
然而,这没有std::function 引入的开销(这是一个虚拟调用),而且 即使在许多情况下也是无法测量的,所以这不应该是引起关注。

【讨论】:

  • 如果你的 lambda 在任何方面都不同 lambda 表达式的类型是唯一类型,每个 lambda is 都不同其他 lambdas(其中 each 表示在特定代码行的特定翻译单元中定义的 lambda)
  • @DavidRodríguez-dribeas:我一开始也是这么想的,但有些人指出链接器可以合并相同的代码,如果它只是类型差异的话。现在检查一下,VC++ 似乎至少部分是这种情况(不过我不知道它是否完全有效);我不知道 GCC。
  • 使用 VS 2010 进行的快速测试似乎表明情况并非如此(可能它是由某些编译器标志驱动的,或者我没有解释符号的转储 --dumpbin /symbols- - 正确。
  • 我无法使用 GCC 4.7 / clang 3.1 和 boost 1.49 编译您的示例代码。编译器没有找到匹配的 operator
  • 就是这样。可能 ADL 把事情搞砸了。 C++11 Lambda -> 35k。 Boost.Lambda 的东西 -> 25k。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-15
  • 1970-01-01
  • 1970-01-01
  • 2020-06-07
  • 1970-01-01
  • 2018-01-19
  • 2014-04-09
相关资源
最近更新 更多