перегрузки c 2 имеют аналогичные преобразования в зависимости от того, является ли оператор функцией-членом или глобальной

#c #operator-overloading #ambiguous

Вопрос:

Рассмотрим простую реализацию векторного класса:

 #include lt;algorithmgt;  class Vector { public:  Vector(int _elementsCount)  : elementsCount(_elementsCount)  , elements(new float[_elementsCount])  {}  ~Vector() {  delete[] elements;  }  Vector(const Vectoramp; rhs) {  elementsCount = rhs.size();  elements = new float[elementsCount];  for (int i = 0; i lt; elementsCount;   i)  (*this)[i] = rhs[i];  }   floatamp; operator [](int i) {  return elements[i];  }  float operator [](int i) const {  return const_castlt;Vectoramp;gt;(*this)[i];  }  int size() const {  return elementsCount;  }  /*// Dot product  float operator *(const Vectoramp; v) {  float res = 0;  for (int i = 0; i lt; size();   i)  res  = (*this)[i] * v[i];  return res;  }*/ private:  int elementsCount;  float* elements; };  // Multiplication by a scalar Vector operator *(const Vectoramp; v, float k) {  Vector res(v.size());  for (int i = 0; i lt; v.size();   i)  res[i] = v[i] * k;  return res; }  // Dot product float operator *(const Vectoramp; v1, const Vectoramp; v2) {  float res = 0;  for (int i = 0; i lt; std::min(v1.size(), v2.size());   i)  res  = v1[i] * v2[i];  return res; }  void main() {  Vector v(2);  v * 3; // ambiguous }  

Этот код компилируется. Но если мы раскомментируем реализацию оператора * в классе и прокомментируем его глобальную реализацию (функцию точечного продукта), то возникнет ошибка «» Вектор::оператор*»: 2 перегрузки имеют аналогичные преобразования», потому что возникает двусмысленность: следует ли вызывать умножение на скаляр или интерпретировать 3 как аргумент параметризованного конструктора и вызывать точечное произведение. В этом есть смысл. Но я не понимаю, в чем разница между объявлением оператора * как функции-члена или как глобальной функции. Я думал, что они должны быть одинаковыми в примере, приведенном выше, но это не так.

Добавлено. Меня больше всего интересует не то, как избежать двусмысленности, а то, почему в одном случае (когда * объявлен как член) существует двусмысленность, а в другом-никого (когда * объявлен как глобальная функция).

Комментарии:

1. Сделайте конструктор explicit , и двусмысленность исчезнет.

2. @Quimby, да, но почему в одном случае есть двусмысленность, а в другом-нет? (Я добавил явный вопрос в конец поста.)

3. Смотрите мой ответ в комментариях к моему ответу. Операторы, которые вы предполагаете эквивалентными, на самом деле не являются таковыми из-за различной постоянства .

Ответ №1:

Вам нужно создать свой конструктор explicit :

 explicit Vector(int _elementsCount) { ... }  

Причина неоднозначности заключается в том , что компилятор не может решить, следует ли ему неявно преобразовывать int значение в a Vector и вызывать Vector::operator* или неявно преобразовывать int значение в a float и использовать operator*(const Vectoramp;, float) .

При использовании explicit такие преобразования запрещены , и вы должны использовать Vector(3) , если хотите, чтобы «3» было a Vector .

В качестве примечания вы должны сделать оператор const , так как он не изменяет объект. Сделав его постоянным, вы также сможете использовать его с const Vector :

 float operator *(const Vectoramp; v) const { ... }  

Будьте осторожны, это все равно будет конфликтовать с вашей другой перегрузкой:

 float operator *(const Vectoramp; v1, const Vectoramp; v2)   

Нет причин иметь и то, и другое. Выберите либо функцию-член, либо глобальную функцию и удалите другую.

Комментарии:

1. Спасибо за ответ. Но можете ли вы сказать, почему неоднозначность зависит от того, как объявлена функция (член против глобального)?

2. Ваши членские и глобальные декларации не эквивалентны. Смотрите мой комментарий о корректности const. Как вы написали, перегрузка вашего члена эквивалентна глобальной перегрузке float operator*(Vectoramp;, const Vectoramp;) …. это означает, что значение слева от * может быть изменено оператором. Если вы определяете свою глобальную перегрузку таким образом, вы получите ту же ошибку. Под капотом компилятор предпочитает сопоставлять const ссылки при использовании операторов.