【问题标题】:Binary Operator for Template Class Doesn't Resolve Implicit Conversion模板类的二元运算符不解决隐式转换
【发布时间】:2019-05-22 17:54:58
【问题描述】:

考虑以下类,其中第一个是模板化的。两者都用于保存数值。

template<typename T>
struct foo
{
    foo(T val) : val{ val } {}
    T val;
};

struct bar
{
    bar(double val) : val{ val } {}
    double val;
};

我想定义一种方法将这些类添加在一起以获得具有不同值的新类。

template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
    return foo<T>(a.val + b.val);
}

bar operator+(bar a, bar b)
{
    return bar(a.val + b.val);
}

当我将这些运算符与隐式转换一起使用时,使用 foo 类型对象的运算符不会对双精度值使用隐式转换来应用我的重载运算符,即使它可以为非模板类​​执行此操作.结果是没有与该表达式中的类型匹配的运算符。

int main()
{
    foo<double> foo_value(11.0);
    bar bar_value(11.0);

    foo<double> ev1 = foo_value + 1.0; // no operator matches these operands
    bar ev2 = bar_value + 1.0;
}

操作符必须先显式实例化吗?如果是这样,a) 看起来如何,以及 b) 如果可以在初始化 foo&lt;double&gt; 类型的对象时完成实例化,为什么不隐式完成?

如果标准不支持任何类型的解析而不明确地将1.0 转换为foo&lt;double&gt; 类型的值,我认为唯一的另一种可能性是为我想要使用的每种类型定义运算符重载(对于左右两边)?

【问题讨论】:

  • bar_value1.0 是不同的类型,不会匹配单个 T1.0 没有隐式转换可以防止模板编程中的很多麻烦。
  • 声明bar ev2 = bar_value + 1.0没有问题;隐式转换用于匹配我定义的运算符。 foo&lt;double&gt; ev2 = foo_value + 1.0 的情况并非如此,即使隐式转换似乎很明显。

标签: c++ templates visual-c++ operator-overloading


【解决方案1】:

关于模板,您必须记住的是,它们不会为您进行转换。他们所做的只是尝试找出事物的类型,如果这与模板参数相匹配,那么它将删除该函数并调用它。

当你这样做时

foo_value + 1.0

编译器没问题,让我们看看我们是否有任何operator + 可以解决这个问题。它发现

template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
    return foo<T>(a.val + b.val);
}

然后它会尝试找出T 是什么,这样它就可以删除一个具体的函数。它查看foo_value,发现它是foo&lt;double&gt;,所以它说第一个参数T 需要是double。然后它查看1.0 并没问题,我有一个double,那就是你遇到问题的时候。编译器无法推断出T 应该是什么b,因为它需要一个foo&lt;some_type&gt;,但是却得到了一个double。因为不能推导出类型,所以你的代码编译失败。

为了获得你想要的行为,你需要添加

template<typename T>
foo<T> operator+(foo<T> a, T b)
{
    return foo<T>(a.val + b);
}

这可以让您将T 添加到foo&lt;T&gt;,或者更好

template<typename T, typename U>
foo<T> operator+(foo<T> a, U b)
{
    return foo<T>(a.val + b);
}

这可以让您向foo&lt;T&gt; 添加任何内容(例如foo&lt;double&gt; + int,第一个版本不允许这样做)

【讨论】:

  • 我试过后者,如果你为 lhs 和 rhs 都定义这样的模板,编译器将不知道它应该使用哪个运算符,这将是一个模棱两可的调用。
  • @Riddick 我必须查看该代码才能告诉您为什么它不起作用。它工作得很好here;
  • 我弄清楚当我接到一个模棱两可的电话时发生了什么:我要求将参数作为父类类型(问题中未显示)。所以我假设因为它不完全适合参数,所以当我传递子类时,添加两个额外的重载会引入歧义。当我投给父母时,它可以工作。
  • @Riddick 听起来很有道理。希望这对您有所帮助。
  • @Riddick 实际的原因是它可以转换成任意数量的东西。假设您有两种可以相互转换的类型(AB),然后您将它们传递给仅采用单一类型的函数模板。编译会将A 转换为B,还是将B 转换为A?试图解决这个问题,以及所有其他边缘情况,会使模板的规则和数量级变得比现在更复杂,而且它们现在非常复杂。
猜你喜欢
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 1970-01-01
  • 2018-01-25
  • 2018-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多