Являются ли шаблонные функции-оболочки для классов (например, std :: make_pair ()) `медленными? - PullRequest
2 голосов
/ 06 марта 2012

Я использую 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 какие-либо другие интересные решения?

Мне нравится эта идея создания шаблонной обёртки функции вокруг конструктора, чтобы мы могли выводить аргументы шаблона, но я не настолько без ума отвоздействие во время выполнения из-за этого.

1 Ответ

3 голосов
/ 06 марта 2012

В моем компиляторе, с оптимизацией и без нее, дополнительный конструктор копирования был оптимизирован.

[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 ++ имеет это сказать при пропуске конструкторов копирования в [class.copy.15] спецификации 2003:

Когда определенные критерии удовлетворены, реализация может опуститьКопировать конструкцию объекта класса, даже если конструктор копирования и / или деструктор для объекта имеют побочные эффекты.В таких случаях реализация рассматривает источник и цель пропущенной операции копирования как просто два разных способа обращения к одному и тому же объекту, и уничтожение этого объекта происходит в более поздние времена, когда два объекта были быуничтожено без оптимизации.) Это исключение операций копирования допускается в следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий):

  • в операторе возврата в функции с типом возврата класса,когда выражение является именем энергонезависимого автоматического объекта с тем же типом cv-unqualified, что и тип возврата функции, операция копирования может быть опущена путем создания автоматического объекта непосредственно в возвращаемое значение функции
  • , когдавременный объект класса, который не был привязан к ссылке (12.2), будет скопирован в объект класса с тем же типом cv-unqualified, операция копирования может быть опущена путем создания временного объекта непосредственно в tАргет опущенной копии
...