Почему универсальный тип, который может быть приведен, не конвертируется неявно?
У меня есть класс 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>&)
и преобразование может произойти, чтобы видеть, жизнеспособна ли функция (и это).