【问题标题】:Class template operator overloading for fundamental and specific non-fundamental types基本和特定非基本类型的类模板运算符重载
【发布时间】:2016-08-26 13:42:09
【问题描述】:

我只是在写一个 MathVector 类

template<typename T> MathVector
{
   using value_type = T;

   // further implementation
};

但是,该类被认为可以与基本类型一起使用,但也可以与复杂类一起使用

template<typename T> Complex
{
   using value_type = T;

   // further implementation
};

例如提供成员函数

template<typename T> Complex<T>& Complex<T>::operator*=(const Complex<T>& c);
template<typename T> Complex<T>& Complex<T>::operator*=(const T& c);

现在,对于 MathVector 类也定义了一个乘法:

template<typename T> MathVector<T>& MathVector<T>::operator*=(const MathVector<T>& c);

这对于T=double 来说很好,但对于T=Complex&lt;double&gt;,我希望能够与double 相乘,而无需先将其转换为Complex&lt;double&gt;(效率更高)。

代码也应该在 CUDA 设备代码中工作这一事实加剧了这种情况(为简洁起见,我省略了说明符 __host__ __device__)。这意味着标准库工具将无济于事。

首先我想到了一个额外的模板参数

template<typename T, typename U> MathVector<T>& MathVector<T>::operator*=(const U& c);

但这对我来说似乎很危险,因为U 可以比TT::value_type 多很多。 (事实上​​,我首先在 Complex 类中也有这个参数 - 编译器无法再决定使用哪个模板,是 Complex 类之一还是 MathVector 类。)

第二个思路是使用模板特化

template<typename T, typename U> MathVector<T>& MathVector<T>::operator*=(const U& c)
{
   static_assert(sizeof(T) == 0, "Error...");
}
template<typename T> MathVector<T>& MathVector<T>::operator*=(const typename T::value_type& c)
{
   // implementation
}

但这将不再适用于基本类型!

我在C++ Operator Overloading for a Matrix Class with Both Real and Complex MatricesReturn double or complex from template function 中看到了这个(或非常相似的)问题的解决方案,但它们是使用标准库以CUDA 无法解决的方式解决的。

所以我的问题是:有没有办法重载适用于基本类型和服务于 value_type 但不适用于其他类型的运算符 - 不使用 nvcc 编译器会拒绝的 std:: 东西?

【问题讨论】:

  • 我不确定这里有什么问题,你可以为它声明所有的重载,比如template&lt;typename T&gt; MathVector&lt;T&gt;&amp; MathVector&lt;T&gt;::operator*=(const MathVector&lt;T&gt;&amp; c);template&lt;typename T&gt; MathVector&lt;T&gt;&amp; MathVector&lt;T&gt;::operator*=(const T&amp; c);template&lt;typename T&gt; MathVector&lt;T&gt;&amp; MathVector&lt;T&gt;::operator*=(const typename T::value_type&amp; c);
  • 您想要U 中的Complex&lt;T&gt;TMathVector&lt;T&gt;MathVector&lt;Complex&lt;T&gt;&gt; 中的4 个重载MathVector&lt;Complex&lt;T&gt;&gt;::operator*=(const U&amp;);
  • @songyuano:问题是重载template&lt;typename T&gt; MathVector&lt;T&gt;&amp; MathVector&lt;T&gt;::operator*=(const typename T::value_type&amp; c); 不会为基本类型编译。 (顺便说一句,与第二个 Mathvector&lt;T&gt; 相乘是没有意义的,因为这将是一个标量积并会返回一个 T,这在 operator*= 中没有意义 - 我为这种情况写了一个 operator*,但这对问题并不重要。)
  • @Jarod42 :请参阅我的第一条评论。我想要MathVector&lt;fundamental_type&gt;::operator*=(const fundamental_type&amp; c&gt;MathVector&lt;Complex&lt;T&gt;&gt;::operator*=(const Complex&lt;T&gt;&amp; c)MathVector&lt;Complex&lt;T&gt;&gt;::operator*=(const T&amp; c)(但更笼统,正如我试图在问题中解释的那样)
  • CUDA如何支持SFINAE/decltype?,如你所愿MathVector&lt;T&gt;operator *= (const U&amp;)T *= U有效

标签: c++ templates cuda operator-overloading


【解决方案1】:

您可以制作operator*=非成员函数模板,并提供所有重载,使SFINAE生效。

template<typename T>
MathVector<T>& operator*=(MathVector<T>& m, const MathVector<T>& c);
template<typename T>
MathVector<T>& operator*=(MathVector<T>& m, const T& c);
template<typename T>
MathVector<T>& operator*=(MathVector<T>& m, const typename T::value_type& c);

然后将它们称为:

MathVector<Complex<double>> m1;
m1 *= MathVector<Complex<double>>{};  // call the 1st one
m1 *= Complex<double>{};              // call the 2nd one
m1 *= 0.0;                            // call the 3rd one

MathVector<double> m2;
m2 *= MathVector<double>{};           // call the 1st one
m2 *= 0.0;                            // call the 2nd one

LIVE

【讨论】:

  • 谢谢你的答案。这是我现在选择的实现。也许有两个注释:(1)第一个重载在这里仍然没有意义,尽管它对于演示编程技术很有用。 (2) 与我的第二次尝试(见问题)相比,关键点似乎是运算符现在是 non-member 函数。但我不知道,为什么这在这里如此重要。也许您可以添加一些有关此的信息。
  • @marlam:SFINAE 适用于模板函数。在模板类中,函数不是模板。
  • @marlam 它不适用于成员函数,因为当实例化类模板(如MathVector&lt;double&gt; m;)时,成员函数(至少它们的声明)也将被实例化,然后编译将失败,因为T=double 的参数类型 T::value_type 无效。非成员函数就没有这样的麻烦,正如 Jarod42 所说,我们必须使它们成为适用于 SFINAE 的模板。
【解决方案2】:

使用 SFINAE 和 decltype,您可以执行类似 (c++11) 的操作:

template<typename T, typename U>
auto
MathVector<T>::operator*=(const U& c)
-> decltype(void(std::declval<T&>() *= c), std::declval<MathVector<T>&>())
{
    // Your implementation
}

【讨论】:

  • 谢谢你的答案。不幸的是,它使用std::declval,因此不能直接应用于问题(CUDA)。因此我更喜欢另一个答案。
  • template &lt;typename T&gt; T declval(); 可能是您的替代品。
猜你喜欢
  • 2013-02-05
  • 2019-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-16
  • 2017-01-20
  • 1970-01-01
  • 2020-07-24
相关资源
最近更新 更多