Подтвердить что ты не робот

Создание шаблонных экземпляров и преобразование типов

Это, если для гуру С++.

Рассмотрим следующий код:

class X { };

template <class T>
class Mistake {
public:
  T x;

  Mistake(const T& k) : x(k) { }
  Mistake(const X&) : x(1) { }

  void print() { cout << x << endl; }
};

template <class T>
Mistake<T> operator+(const Mistake<T>& a, const Mistake<T>& b)
{
  return Mistake<T>(a.x + b.x);
}

У меня есть класс "Ошибка", для которого я хочу выполнить операцию добавления. Когда я пытаюсь:

X a, b;
Mistake<int> foo = a + b;

Я получаю ошибку компиляции; компилятор не может понять, что оператор шаблона + должен быть создан.

Если, с другой стороны, я добавляю следующий фрагмент кода:

Mistake<int> operator+(const Mistake<int>& a, const Mistake<int>& b)
{
  return Mistake<int>(a.x + b.x);
}

тогда все хорошо. Кто-нибудь знает почему? Я подозреваю, что компилятор не может понять, что нужно создать из-за преобразования типа, необходимого из класса X в класс Ошибки, но я не знаю, как исправить эту проблему, не используя шаблоны вообще.

Кстати, определение оператора внутри класса как друга также не работает.

Спасибо!

4b9b3361

Ответ 1

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

Проблема заключается в том, что пользовательские преобразования не учитываются при выполнении вывода типа. Когда компилятору представлено это выражение:

a + b

Если оба a и b имеют тип X и следующую подпись для operator +:

template <class T>
Mistake<T> operator+(const Mistake<T>& a, const Mistake<T>& b)

Первое, что сделает компилятор, это попытаться вывести T так, чтобы типы параметров оператора соответствовали типам аргументов. Если это невозможно, компилятор немедленно отказывается, не рассматривая возможные конструкторы преобразования, и фокусируется на других функциях-кандидатах (или шаблонах функций).

Учитывая вышеизложенную ситуацию, ясно, что нет возможности сделать Mistake<T> exaccty X, независимо от того, что вы выбрали (Mistake<int> не X, Mistake<X> не X и т.д.). Таким образом, замена не выполняется, и компилятор не знает, как разрешить вызов, потому что других кандидатов нет.

С другой стороны, когда у вас есть это:

Mistake<int> operator+(const Mistake<int>& a, const Mistake<int>& b)

Не существует никакого типа дедукции, потому что выше это не шаблон функции. Поэтому компилятор учитывает определенные пользователем преобразования при попытке разрешить вызов, а поскольку Mistake<int> имеет конструктор, который принимает X, вышеуказанный operator + считается жизнеспособным кандидатом, и он выбирается.

Ответ 2

Я не думаю, что есть способ. Самое чистое, что вы можете достичь -

Mistake<int> foo = static_cast<Mistake<int>>(a) + static_cast<Mistake<int>>(b);

Или, если вы немного нажмете его, используя дополнительную перегрузку, которая соответствует типам ассиметричных операндов:

template <class T, class U>
Mistake<T> operator+(const Mistake<T>& a, U const& b) {
    return a + static_cast<Mistake<T>>(b);
}

    // and later:
    foo = Mistake<int>(a) + b;

Полная демо-версия: http://ideone.com/ki14GO

#include <iostream>

class X { };

template <class T>
class Mistake {
    public:
        T x;

        Mistake(const T& k) : x(k) { }
        Mistake(const X&) : x(1) { }

        void print() { std::cout << x << std::endl; }
};

template <class T>
Mistake<T> operator+(const Mistake<T>& a, const Mistake<T>& b) {
    return Mistake<T>(a.x + b.x);
}

template <class T, class U>
Mistake<T> operator+(const Mistake<T>& a, U const& b) {
    return a + static_cast<Mistake<T>>(b);
}

template <class T, class U>
Mistake<T> operator+(const U& a, Mistake<T> const& b) {
    return static_cast<Mistake<T>>(a) + b;
}

int main()
{
    X a, b;
    Mistake<int> foo = static_cast<Mistake<int>>(a) + static_cast<Mistake<int>>(b);
    foo = Mistake<int>(a) + b;
    foo = a + Mistake<int>(b);
}

Ответ 3

Я считаю, что у компилятора есть проблема с выводом типа a + b.

Вы можете определить:

X operator+(const X & a, const  X & b) {
   return a /* ??? or something else */;
}

если у вас есть способ сказать, что ответ на a + b есть в терминах X.