Jusqu'à un test que je viens de faire, je pensais que seuls les constructeurs n'étaient pas hérités en C++. Mais apparemment, la mission operator=
n'est pas trop ...
operator+=
, operator-=
, ...?En fait, j'ai rencontré ce problème pendant que je faisais du CRTP:
template<class Crtp> class Base
{
inline Crtp& operator=(const Base<Crtp>& rhs) {/*SOMETHING*/; return static_cast<Crtp&>(*this);}
};
class Derived1 : public Base<Derived1>
{
};
class Derived2 : public Base<Derived2>
{
};
Y a-t-il une solution pour que cela fonctionne?
EDIT: OK, j'ai isolé le problème. Pourquoi ce qui suit ne fonctionne pas? Comment résoudre le problème ?
#include <iostream>
#include <type_traits>
// Base class
template<template<typename, unsigned int> class CRTP, typename T, unsigned int N> class Base
{
// Cast to base
public:
inline Base<CRTP, T, N>& operator()()
{
return *this;
}
// Operator =
public:
template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
inline CRTP<T, N>& operator=(const T0& rhs)
{
for (unsigned int i = 0; i < N; ++i) {
_data[i] = rhs;
}
return static_cast<CRTP<T, N>&>(*this);
}
// Data members
protected:
T _data[N];
};
// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
};
// Main
int main()
{
Derived<double, 3> x;
x() = 3; // <- This is OK
x = 3; // <- error: no match for 'operator=' in ' x=3 '
return 0;
}
L'opérateur d'affectation est techniquement hérité; cependant, il est toujours masqué par un opérateur d'assignation défini explicitement ou implicitement pour la classe dérivée (voir les commentaires ci-dessous).
(13.5.3 Assignation) Un opérateur d'assignation doit être implémenté par une fonction membre non statique avec exactement un paramètre. Parce qu'un opérateur d'affectation de copie
operator=
est implicitement déclaré pour une classe s'il n'est pas déclaré par l'utilisateur, un opérateur d'affectation de classe de base est toujours masqué par l'opérateur d'affectation de copie de la classe dérivée.
Vous pouvez implémenter un opérateur d'affectation factice qui transfère simplement l'appel à la classe de base operator=
, comme ça:
// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
public:
template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
inline Derived& operator=(const T0& rhs)
{
return Base<Derived, T, N>::operator=(rhs);
}
};
L'opérateur d'affectation est hérité, en quelque sorte, mais ... Dans une classe donnée, si vous ne fournissez pas d'opérateur d'affectation de copie, le compilateur en génère un pour vous. Cela signifie que vos classes dérivées ont effectivement un opérateur d'affectation:
Derived& operator=( Derived const& );
Et les règles de masquage habituelles s'appliquent; cela masque tous les opérateurs d'affectation de classe de base. (Si la classe de base avait un opérateur d'affectation avec cette signature, la classe dérivée en hériterait normalement.)
Votre opérateur d'affectation est techniquement hérité, mais il est ensuite masqué par l'opérateur d'affectation de copie par défaut dans la classe dérivée. Cette affectation de copie par défaut essaie ensuite d'appeler l'affectation de copie de la classe de base qui n'existe pas puisque vous l'avez cachée avec votre propre affectation.
La façon la plus saine de résoudre ce problème est de ne pas utiliser la surcharge d'opérateur de manière non évidente (=
ne signifie pas affectation de copie par exemple). Dans ce cas, n'utilisez pas operator=
: Appelez-le quelque chose comme assign
ou set
, puis il héritera et ne sera pas masqué par l'affectation de copie enfant.
Ces opérateurs sont hérités et il n'y a pas de version de compilateur, ils ne seront donc jamais automatiquement masqués comme operator=
.
Ce ne sont vraiment que des constructeurs qui ne sont pas hérités, et je ne peux penser à aucune autre fonction générée par le compilateur qui pourrait cacher quelque chose au parent comme dans operator=
.