模板化运算符实例化和类型转换

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+。但是您正在尝试添加两个Xs @Named 没关系,因为有一个 Mistake 构造函数接受 X &amp; @Named:我试图解释我的回答中发生了什么 @wroniasty 如果这样的转换有效,那么很多事情都不会有效,例如std::is_floating_point,因为ints 被提升为floats 等等。但我认为@Andy 解释得更好。 【参考方案1】:

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

这里的问题是执行类型推导时没有考虑用户定义的转换。当编译器出现这个表达式时:

a + b

ab 的类型均为 Xoperator + 的签名如下:

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

编译器要做的第一件事是尝试推导T,以便运算符的参数类型与参数类型exaclty相匹配。如果这不可能,编译器立即放弃,不考虑可能的转换构造函数,并专注于其他候选函数(或函数模板)。

考虑到上面的情况,很明显没有办法让Mistake&lt;T&gt;变成exaclty X,无论你选择什么TMistake&lt;int&gt;不是XMistake&lt;X&gt; 不是 X,以此类推)。因此,替换失败,编译器不知道如何解决调用,因为周围没有其他候选者。

另一方面,当你有这个时:

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

不涉及类型推导,因为上面不是函数模板。因此,编译器在尝试解析调用时考虑用户定义的转换,并且由于Mistake&lt;int&gt; 有一个接受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 的回答以获得更深入的解释。

以上是关于模板化运算符实例化和类型转换的主要内容,如果未能解决你的问题,请参考以下文章

在统一过程中,高级类型的实例化和包含如何交互?

2020春招字节跳动二面

C++模板类模板的全部特例化和局部特例化(偏特化-partial specialization)

为啥我需要这个 CString 类型转换?

C++ template —— 实例化和模板实参演绎

NPE,同时访问实例化和构造函数配置的不可变对象的原始类型(双精度)。 (不涉及自动装箱或反射)