【问题标题】:Why does a generic type that can be casted not get implicitly converted?为什么可以强制转换的泛型类型不会被隐式转换?
【发布时间】:2018-12-22 17:08:55
【问题描述】:

我有一个类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;
}

这是为什么?使运算符重载泛型是否意味着无法推断类型?

【问题讨论】:

  • C++ 中没有“通用”类,ABA&lt;T&gt;B&lt;T&gt; 都不是类型。
  • 是的,我的术语可能不正确,但我希望意思清楚。

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


【解决方案1】:

一般来说,在进行参数推导时不允许隐式转换,我可以认为派生到基数是允许的。表达式

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

但我不会那样做,坚持演员表可以更好地解释意图。

【讨论】:

  • 是的,显式可能更好,我只是想知道为什么它不能用模板完成,但我想在推导参数时不可能进行隐式转换是有道理的。
  • 选择重载的方法不是这样:我们不期望重载。我们考虑重载(ADL,...),过滤可行的重载(这里,OP 的重载被丢弃),然后选择最好的一个。
【解决方案2】:

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

【讨论】:

  • operator*“属于”B,而不是A。并且应该删除模板operator*
【解决方案3】:

在推论过程中,不会发生转换/提升,所以对于

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&lt;int&gt; operator*(const B&lt;int&gt;&amp;, const B&lt;int&gt;&amp;) 并且可以进行转换以查看函数是否可行(确实可行)。

Demo

【讨论】:

    猜你喜欢
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多