模板化运算符实例化和类型转换
Posted
技术标签:
【中文标题】模板化运算符实例化和类型转换【英文标题】:Templated operator instantiation and type conversion 【发布时间】:2013-05-05 12:33:47 【问题描述】:如果是 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;
我得到一个编译错误;编译器似乎无法意识到模板 operator+ 必须被实例化。
另一方面,如果我在之前添加以下代码:
Mistake<int> operator+(const Mistake<int>& a, const Mistake<int>& b)
return Mistake<int>(a.x + b.x);
那么一切都很好。有人知道为什么吗?我怀疑编译器无法确定要实例化什么,因为需要从类 X 到类 Mistake 的类型转换,但我不知道如何解决这个问题,除非根本不使用模板。
顺便说一句,将类中的操作符定义为友元也行不通。
谢谢!
【问题讨论】:
我在这里遗漏了什么吗?你没有为X
重载operator+
。但是您正在尝试添加两个X
s
@Named 没关系,因为有一个 Mistake
构造函数接受 X &
@Named:我试图解释我的回答中发生了什么
@wroniasty 如果这样的转换有效,那么很多事情都不会有效,例如std::is_floating_point
,因为int
s 被提升为float
s 等等。但我认为@Andy 解释得更好。
【参考方案1】:
虽然其他人已经为您的问题提出了可能的解决方案,但我想指出发生了什么,以及为什么无法满足您的期望。
这里的问题是执行类型推导时没有考虑用户定义的转换。当编译器出现这个表达式时:
a + b
a
和 b
的类型均为 X
,operator +
的签名如下:
template <class T>
Mistake<T> operator+(const Mistake<T>& a, const Mistake<T>& b)
编译器要做的第一件事是尝试推导T
,以便运算符的参数类型与参数类型exaclty相匹配。如果这不可能,编译器立即放弃,不考虑可能的转换构造函数,并专注于其他候选函数(或函数模板)。
考虑到上面的情况,很明显没有办法让Mistake<T>
变成exaclty X
,无论你选择什么T
(Mistake<int>
不是X
, Mistake<X>
不是 X
,以此类推)。因此,替换失败,编译器不知道如何解决调用,因为周围没有其他候选者。
另一方面,当你有这个时:
Mistake<int> operator+(const Mistake<int>& a, const Mistake<int>& b)
不涉及类型推导,因为上面不是函数模板。因此,编译器在尝试解析调用时将考虑用户定义的转换,并且由于Mistake<int>
有一个接受X
的构造函数,因此上述operator +
被认为是可行的候选人,并被选中。
【讨论】:
我看到了对这个问题的所有支持,并犹豫要不要回答这个问题,因为我认为我错过了一些东西。我应该回答它:D。无论如何 +1 一如既往。 @Named:下一次,就这样吧;) 但有一个问题是它只有user-defined conversions
没有考虑或任何 转换。因为 float
特化不能与 int
arg 匹配,即使 int
可以转换为 float
。这不是用户定义的转换。没有?
@Named:不,一般不考虑转化,但也有例外。但是,用户定义的转换从不考虑在内。
@Named:即,将派生到基础和 cv-qualification 调整考虑在内。【参考方案2】:
我认为没有办法。你能做到的最纯粹的是
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);
【讨论】:
【参考方案3】:我认为编译器在推断a + b
的类型时有问题。
你可以定义:
X operator+(const X & a, const X & b)
return a /* ??? or something else */;
如果您有任何方法可以判断a + b
的答案是什么X
。
【讨论】:
查看下面的 Andy Prowl 的回答以获得更深入的解释。以上是关于模板化运算符实例化和类型转换的主要内容,如果未能解决你的问题,请参考以下文章