Почему универсальный тип, который может быть приведен, не конвертируется неявно?

У меня есть класс A и класс Bоба являются общими с параметром типа T, Объект A<T> может быть приведен к B<T>, У меня есть перегрузка общего оператора на B что я хочу иметь возможность позвонить на A объект и B объект, где A объект неявно преобразуется.

Когда я пытаюсь это не компилируется:

template <typename T>
class A {};

template <typename T>
class B {
public:
    B() {}
    B(const A<T> &a) {}
};

template <typename T>
B<T> operator*(const B<T> &obj1, const B<T> &obj2) {
    return B<T>(); // doesn't matter
}

int main() {
    A<int> objA;
    B<int> objB;

    B<int> combined1 = objA * objB; // error: operator* isn't defined on these types
    B<int> combined2 = static_cast<B<int>>(objA) * objB; // fine

    return 0;
}

Однако, когда A и B не являются общими, они работают нормально:

class A {};

class B {
public:
    B() {}
    B(const A &a) {}
};

B operator*(const B &obj1, const B &obj2) {
    return B(); // doesn't matter
}

int main() {
    A objA;
    B objB;

    B combined1 = objA * objB; // fine
    B combined2 = static_cast<B>(objA) * objB; // also fine

    return 0;
}

Почему это? Есть ли что-то в создании универсальной перегрузки оператора, которая означает, что тип не может быть выведен?

3 ответа

Решение

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

B<int> combined1 =  objA * objB;

ожидать найти жизнеспособные перегрузки для objA * objBВ том числе найденные ADL, одним из возможных является:

template <typename T>
B<T> operator*(const A<T> &obj1, const B<T> &obj2) {...}

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

 B<int> combined1 = operator*<int>(objA, objB);

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

Вы можете определить функцию друга в class A который вызывает вашу функцию шаблона

template <class T>
class B;

template <typename T>
class A {
    friend B<T> operator*(const B<T> &obj1, const B<T> &obj2) {} # here call template function
};

template <typename T>
class B {
public:
    B() {}
    B(const A<T> &a) {}

};

template <typename T>
B<T> operator*(const B<T> &obj1, const B<T> &obj2) {
    return B<T>(); // doesn't matter
}

int main() {
    A<int> objA;
    B<int> objB;

    B<int> combined1 = objA * objB; // fine
    B<int> combined2 = static_cast<B<int>>(objA) * objB; // fine

    return 0;
}

Во время вывода аргумента конверсия / продвижение не происходит, поэтому для

objA * objB

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

template <typename T> B<T> operator*(const B<T> &, const B<T> &);

Так что перегрузка отклонена.

Один из способов исправить это - создать не шаблонную функцию. Asit должен применяться к шаблону classe, один из способов сделать это friend функции:

template <typename T>
class B {
public:
    B() {}
    B(const A<T>&) {}

    friend B operator*(const B&, const B&) { return /*...*/; }
};

Сейчас, objA * objB считает перегрузку B<int> operator*(const B<int>&, const B<int>&) и преобразование может произойти, чтобы видеть, жизнеспособна ли функция (и это).

демонстрация

Другие вопросы по тегам