类的模板化包装器函数(例如std::make_pair())是否被认为很慢

Are templated wrapper functions for classes (e.g. std::make_pair())` considered slow?

本文关键字:make pair 是否 被认为 例如 包装 函数 std      更新时间:2023-10-16

我在这个例子中使用std::make_pair(),因为几乎任何C++程序员都应该熟悉它,但更普遍的是,我想知道它使用的模式。

我突然想到,尽管我喜欢std::make_pair()的便利性,但它会为每个参数制作一个"额外"的副本,因为它会创建一对并按值返回。如果我用它插入STL容器,这意味着实际上每个参数总共复制了3次。。。我写了这个代码片段来说明(以及在不失去太多便利的情况下改进它的一些尝试):

#include <iostream>
#include <utility>
#include <list>
using namespace std;
// C++11 only:
#define MAKE_PAIR(a,b) pair<decltype(a),decltype(b)>((a),(b))
class A {
public:
  A () { }
  A (const A& a) {
    cout << "tCopy constructor called" << endl;
  }
};
int main()
{
  list<pair<int,A> > l;
  cout << "Using std::make_pair()" << endl;
  l.push_back(make_pair(10,A()));
  cout << "Using MAKE_PAIR()" << endl;
  l.push_back(MAKE_PAIR(10,A()));
  typedef pair<int, A> my_pair;
  cout << "Using a typedef" << endl;
  l.push_back(my_pair(10,A()));
}

产生输出:

Using std::make_pair()
    Copy constructor called
    Copy constructor called
    Copy constructor called
Using MAKE_PAIR()
    Copy constructor called
    Copy constructor called
Using a typedef
    Copy constructor called
    Copy constructor called

我意识到这里还有一些其他副本可能会被消除(或者更确切地说,减少为指针/智能指针副本),例如,在这对副本中使用a*或智能指针,然后自己分配。

宏的想法(需要C++11)对我来说似乎很有趣,尽管我知道很多人不喜欢宏。typedef也很好用,但您必须为每组模板参数创建一个单独的typedef,因此它比每次显式指定模板参数更方便,它仍然不如包装器函数好。

我想知道有人真的因为这个原因而在实践中避开make_pair()吗?C++/C++11是否提供了其他有趣的解决方案?

我喜欢在构造函数周围创建一个模板化函数包装器的想法,这样我们就可以推导出模板参数,但我并没有因此而对运行时产生影响

在我的编译器中,无论有没有优化,额外的复制构造函数都被优化掉了。

[10:53pm][wlynch@orange /tmp] c++ --version
Apple clang version 3.1 (tags/Apple/clang-318.0.45) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin11.3.0
Thread model: posix
[10:54pm][wlynch@orange /tmp] c++ -O0 -std=gnu++11 foo.cc -o foo
[10:54pm][wlynch@orange /tmp] ./foo
Using std::make_pair()
    Copy constructor called
    Copy constructor called
Using MAKE_PAIR()
    Copy constructor called
    Copy constructor called
Using a typedef
    Copy constructor called
    Copy constructor called

如果我添加参数-fno-elide-constructors,那么我们会看到额外的构造函数。

[10:57pm][wlynch@orange /tmp] c++ -std=gnu++11 -fno-elide-constructors foo.cc -o foo
[10:57pm][wlynch@orange /tmp] ./foo
Using std::make_pair()
    Copy constructor called
    Copy constructor called
    Copy constructor called
    Copy constructor called
Using MAKE_PAIR()
    Copy constructor called
    Copy constructor called
Using a typedef
    Copy constructor called
    Copy constructor called

C++规范对跳过2003规范的[class.copy.15]中的复制构造函数有这样的看法

当满足某些条件时,允许实现省略类对象的复制构造,即使该对象的复制构造函数和/或析构函数有副作用。在这种情况下,实现将省略的复制操作的源和目标视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象在没有优化的情况下被销毁的较晚时间。)在以下情况下允许省略复制操作(可以组合以消除多个副本):

  • 在具有类返回类型的函数中的return语句中,当表达式是与函数返回类型具有相同cv不合格类型的非易失性自动对象的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制操作
  • 当尚未绑定到引用(12.2)的临时类对象将被复制到具有相同cv不合格类型的类对象时,可以通过将临时对象直接构造到被省略的复制的目标中来省略复制操作