【问题标题】: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 解释得更好。

标签: c++ templates overloading operator-keyword


【解决方案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 的回答以获得更深入的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 2020-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多