El consenso actual es que debe implementar primero todos sus operadores = que no creen objetos nuevos. Dependiendo de si la seguridad de la excepción es un problema (en su caso, probablemente no lo es) o un objetivo, la definición de operador = puede ser diferente. Después de eso, ¿implementar operador? como una función libre en términos del operador? = usando semántica 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;
}
Esto tiene las siguientes ventajas:
- Sólo una implementación de la lógica. Si la clase cambia solo necesita volver a implementar el operador? = Y el operador? se adaptará automáticamente
- El operador de función libre es simétrico con respecto a las conversiones de compilador implícitas
- ¿Es la implementación más eficiente del operador? usted puede encontrar con respecto a las copias
¿Eficiencia del operador?
¿Cuándo llamas al operador? en dos elementos, se debe crear y devolver un tercer objeto. Usando el enfoque anterior, la copia se realiza en la llamada al método. Tal como está, el compilador puede elidear la copia cuando está pasando un objeto temporal. Tenga en cuenta que esto debe leerse como 'el compilador sabe que puede elide la copia', no como 'el compilador será elide la copia'. El kilometraje variará con los diferentes compiladores, e incluso el mismo compilador puede arrojar resultados diferentes en diferentes ejecuciones de compilación (debido a diferentes parámetros o recursos disponibles para el optimizador).
En el siguiente código, un temporal se creará con la suma de a
y b
, y que temporalmente se deben pasar de nuevo a operator+
junto con c
para crear un segundo temporal con el resultado final:
Q a, b, c;
// initialize values
Q d = a + b + c;
Si operator+
tiene una semántica de paso por valor, el compilador puede elide la copia de valor por paso (el compilador sabe que el temporal será destruido inmediatamente después de la segunda llamada operator+
, y no necesita crear una copia diferente para pasar)
Incluso si el operator?
pudiera implementarse como una función de línea (Q operator+(Q lhs, Q const & rhs) { return lhs+=rhs; }
) en el código, no debería ser así. La razón es que el compilador no puede saber si la referencia devuelta por operator?=
es de hecho una referencia al mismo objeto o no. Al hacer que la declaración return explícitamente tome el objeto lhs
, el compilador sabe que la copia devuelta puede ser eliminada.
simetría con respecto a los tipos de
si hay una conversión implícita de tipo T
para escribir Q
, y tiene dos instancias t
y q
, respectivamente, de cada tipo, entonces se esperaría (t+q)
y (q+t)
tanto estar invocable Si implementa operator+
como una función miembro dentro de Q
, el compilador no podrá convertir el objeto t
en un objeto temporal Q
y luego llamará al (Q(t)+q)
ya que no puede realizar conversiones de tipo en el lado izquierdo para llamar a una función miembro. Por lo tanto, con una función miembro, la implementación t+q
no se compilará.
Tenga en cuenta que esto también es cierto para los operadores que no son simétricos en términos aritméticos, estamos hablando de tipos. Si puede sustraer un T
desde un Q
promocionando el T
a un , entonces no hay razón para no poder sustraer un Q
de un T
con otra promoción automática.
No sé de dónde viene __forceinline, pero ciertamente no es estándar C++ –
es específico del compilador. Simplemente obliga al compilador a hacerlo en línea. – Mark
No hay nada que podamos decir sobre esto sin saber cuál es tu compilador, ya que todo depende del compilador. –