定义模板化操作符重载时出错

Error defining a templated operator overload

本文关键字:重载 出错 操作符 定义      更新时间:2023-10-16

这是一个操作符+的模板化重载尝试。在gcc 4.8和icc 14.0.3下都无法编译。

template <typename T>
class B
{
public:
  B operator+(const B& rhs)
  {
    return *this;
  }
};
template <typename T>
class A
{
public:
  operator B<T>() const{return B<T>();}
};
// template<typename T>                                                                                                                                                        
// B<T> operator+(A<T> const& t, A<T> const& u)                                                                                                                                
// {                                                                                                                                                                           
//   return (B<T>)t + (B<T>)u;                                                                                                                                                 
// }                                                                                                                                                                           
template<typename T, typename U>
B<U> operator+(A<T> const& t, A<T> const& u)
{
  return (B<U>)t + (B<U>)u;
}
int main()
{
  A<double> a,b;
  B<double> c = a+b;
  return 0;
}

但是,注释的重载工作得很好。有什么区别呢?为什么有两个参数的模板不匹配?

g++48 -std=c++11 temp2.cpp
temp2.cpp: In function ‘int main()’:
temp2.cpp:33:18: error: no match for ‘operator+’ (operand types are ‘A<double>’ and ‘A<double>’)
   B<double> c = a+b;
                  ^
temp2.cpp:33:18: note: candidate is:
temp2.cpp:25:6: note: template<class T, class U> B<U> operator+(const A<T>&, const A<T>&)
 B<U> operator+(A<T> const& t, A<T> const& u)
      ^
temp2.cpp:25:6: note:   template argument deduction/substitution failed:
temp2.cpp:33:19: note:   couldn't deduce template parameter ‘U’
   B<double> c = a+b;

编译器告诉你失败的原因:

temp2.cpp:25:6:注意:模板参数演绎/替换失败:
temp2.cpp:33:19:注意:无法推断模板参数' U '

模板参数U只出现在函数模板的返回类型中,因此无法推导。如果显式列出模板参数

,代码将编译成功。
B<double> c = operator+<double, double>(a, b);

如果你交换模板参数的顺序,使U出现在T之前,你仍然可以允许T被推导出来。

template<typename U, typename T>
B<U> operator+(A<T> const& t, A<T> const& u)
{
  return (B<U>)t + (B<U>)u;
}
B<double> c = operator+<double>(a, b);

注释掉的operator+实现有效,因为返回类型也使用相同的类型参数T,因此允许它从函数模板参数中推导出来。

In

B<double> c = a+b;

中的类型模板参数U
B<U> operator+(A<T> const& t, A<T> const& u)

不能推导。U不会被推断为double,因为调用的结果被赋值给B<double>。您必须显式地将U指定为double,例如:

B<double> c = operator+<double, double>(a, b);

显然,这可能不是一个理想的情况。那么你能做些什么呢?嗯,很难说,因为您没有指定AB要用于什么。但是,正如您已经发现的那样,您的代码使用注释掉的操作符

进行编译。
template<typename T>
B<T> operator+(A<T> const& t, A<T> const& u)
{
    return (B<T>)t + (B<T>)u;
}

出于某种原因,您似乎希望可以使用结果来初始化B<U>,其中U可能与T不同,因此也许正确的解决方案是使B<T>可以构造B<U>:

template <typename T>
class B
{
public:
  template <typename U>
  B(const B<U>& rhs) {
    // ...
  }
  // ...
};

(您可能还想编写类似的赋值操作符)

来自编译器的错误信息是清楚的。无法推导参数U来实例化operator+函数。

可以使用:

B<double> c = operator+<double, double>(a,b);

没有推导返回类型。

你可以用表达式模板来代替。

template<template<class>class Op, class Rhs, class Lhs>
struct deferred{
    Lhs lhs; Rhs rhs;
    template<typename Result>
    operator Result() const { return Op<Result>{}( std::forward<Lhs>(lhs), std::forward<Rhs>(rhs) ); }
};
template<class R> sum;
template<class U> sum<B<U>>{
  template<class Lhs, class Rhs>
  B<U> operator()( A<Lhs> const& lhs, A<Rhs> const& rhs )const{
    return B<Lhs>(lhs)+B<Rhs>(rhs);
  }
};
template<class T>
deferred<sum, A<T>const&, A<T>const&> operator+( A<T>const& a, A<T>const& b){
  return {a,b};
}