为什么不能强制转换的泛型类型无法隐式转换?

时间:2018-12-22 17:08:56

标签: c++ templates operator-overloading implicit-conversion

我有一个类A和一个类B,它们都具有类型参数T的泛型。 A<T>的对象可以强制转换为B<T>。我在B上有一个通用的运算符重载,希望能够在A对象进行隐式转换的B对象和A对象上调用。 / p>

当我尝试这样做时,它不会编译:

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 个答案:

答案 0 :(得分:1)

通常,在进行参数推导时不允许进行隐式转换,我可以认为 base 是允许的。表达式

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);

但是我不会那样做,坚持使用演员表可以更好地说明意图。

答案 1 :(得分:0)

您可以在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;
}

答案 2 :(得分:0)

在推论参数期间,不会发生转换/提升,因此对于

objA * objB

在检查候选超载的有效性时,无法推论T

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

这样过载就被拒绝了。

一种解决方法是创建一个非模板函数。 Asit应该应用于分类模板,一种实现方法是使用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>&),可以进行转换以查看函数是否可行(确实如此)。

Demo