Нынешний консенсус заключается в том, что вы должны реализовать сначала все ваши операторы = =, которые не создают новые объекты. В зависимости от того, является ли безопасность исключений проблемой (в вашем случае это, вероятно, нет) или целью определение оператора? = Может быть другим. После этого вы выполняете оператор? как свободную функцию в терминах оператора? = с использованием семантики pass-by-value.
// thread safety is not a problem
class Q
{
double w,x,y,z;
public:
// constructors, other operators, other methods... omitted
Q& operator+=(Q const & rhs) {
w += rhs.w;
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
};
Q operator+(Q lhs, Q const & rhs) {
lhs += rhs;
return lhs;
}
Это имеет следующие преимущества:
- только одна реализация логики. Если класс изменяется, вам нужно только переопределить оператор? = И оператор? будет адаптироваться автоматически.
- Оператор свободной функции симметричен относительно неявных преобразований компилятора
- Это наиболее эффективная реализация оператора? вы можете найти в отношении копий
Эффективность оператора?
Когда вы вызываете оператора? на двух элементах должен быть создан и возвращен третий объект. Используя вышеприведенный подход, копия выполняется в вызове метода. Как бы то ни было, компилятор может удалять копию при передаче временного объекта. Обратите внимание, что это должно быть прочитано как «компилятор знает, что он может исключить копию ', а не как' компилятор будет elide the copy '. Пробег будет варьироваться в зависимости от разных компиляторов, и даже тот же самый компилятор может давать разные результаты в разных записях компиляции (из-за разных параметров или ресурсов, доступных для оптимизатора).
В следующем коде, временный будет создан с суммой a
и b
, и что временное должно быть передано снова operator+
вместе с c
создать второй временный с конечным результатом:
Q a, b, c;
// initialize values
Q d = a + b + c;
Если operator+
имеет значение semantics, то компилятор может исключить экземпляр pass-by-value (компилятор знает, что временное будет разрушено сразу после второго вызова operator+
и не нужно будет создавать другую копию для передачи)
Даже если operator?
может быть реализован как функция одной строки (Q operator+(Q lhs, Q const & rhs) { return lhs+=rhs; }
) в коде, это не должно быть так. Причина в том, что компилятор не может знать, является ли ссылка, возвращаемая operator?=
, фактически ссылкой на тот же объект или нет. Если оператор return явно принимает объект lhs
, компилятор знает, что возвратная копия может быть удалена.
Симметрия относительно типов
Если существует неявное преобразование типа T
к типу Q
, и у вас есть два экземпляра t
и q
соответственно каждого типа, то вы ожидаете (t+q)
и (q+t)
как быть отозваны. Если вы реализуете operator+
в качестве функции-члена внутри Q
, тогда компилятор не сможет преобразовать объект t
во временный объект Q
, а затем вызвать (Q(t)+q)
, поскольку он не может выполнять преобразования типов в левой части для вызова функции-члена. Таким образом, с помощью реализации функции-члена t+q
не будет компилироваться.
Обратите внимание, что это справедливо и для операторов, которые не являются симметричными в арифметических терминах, мы говорим о типах. Если вы можете вычесть T
от Q
, рекламируя T
до Q
, тогда нет причин, чтобы не вычитать Q
от T
с другой автоматической раскруткой.
Я не знаю, откуда __forceinline, но он, конечно, не является стандартным C++ – 2009-08-21 19:13:16
его компилятор специфичен. Это просто заставляет компилятор сделать его встроенным. – Mark
Мы ничего не можем сказать об этом, не зная, что такое ваш компилятор, поскольку это все зависит от компилятора. –