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

时间:2012-03-06 04:46:44

标签: c++ performance templates copy c++11

我在这个例子中使用std::make_pair(),因为几乎任何C ++程序员都应该熟悉它,但更常见的是我对它使用的模式感到疑惑。

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

#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 *或智能指针,并自己分配它。 / p>

宏观想法(需要C ++ 11)对我来说似乎很有趣,虽然我知道很多人不喜欢宏。 typedef也可以正常工作,但是你必须为每组模板args创建一个单独的typedef,因此它比每次显式指定模板args更方便,它仍然不如包装函数那么好。

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

我喜欢在构造函数周围创建模板化函数包装器的想法,这样我们就可以推导出模板参数,但是我并没有因为它而导致运行时影响。

1 个答案:

答案 0 :(得分:3)

在我的编译器中,无论是否进行优化,都会优化额外的复制构造函数。

[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 ++ Spec在2003规范的[class.copy.15]中跳过复制构造函数时有这个说法:

  

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

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