模板化操作符实例化和类型转换

Templated operator instantiation and type conversion

本文关键字:类型转换 实例化 操作符      更新时间:2023-10-16

这是写给那些c++高手的

考虑以下代码:

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到类错误需要类型转换,但我不知道如何解决这个问题,除非根本不使用模板。

顺便说一下,在类内部将操作符定义为友元也是不行的。

谢谢!

虽然其他人已经对你的问题提出了可能的解决方案,但我想指出发生了什么,以及为什么你的期望不能满足。

这里的问题是在执行类型推导时不考虑用户定义的转换。当编译器看到这个表达式时:
a + b

其中ab均为X类型,operator +的签名如下:

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

编译器要做的第一件事是尝试推导T,使操作符的形参类型与实参类型完全匹配。如果这是不可能的,编译器立即放弃,不考虑可能的转换构造函数,并关注其他候选函数(或函数模板)。

考虑到上面的情况,很明显,没有办法使Mistake<T>变成恰好 X,无论你选择的T是什么(Mistake<int>不是X, Mistake<X>不是X,等等)。因此,替换失败,编译器不知道如何解析调用,因为周围没有其他候选对象。

另一方面,当你有这个:

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

不涉及类型演绎,因为上面不是函数模板。因此,编译器在尝试解析调用时将考虑用户定义的转换,并且由于Mistake<int>有一个接受X的构造函数,因此上面的operator +被认为是可行的候选者,并且它被选中。

我认为没有办法。你能做到的最纯粹的是

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

我认为编译器在推断a + b的类型时存在问题。

你可以定义:

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

如果你有办法告诉a + b的答案是什么X