【问题标题】:Operator Overload for Template Class Auto Type Conversion模板类自动类型转换的运算符重载
【发布时间】:2014-01-16 03:08:10
【问题描述】:

我有一个模板类,我正在为其重载operator+,但需要它可能返回与该类实例化的数据类型不同的数据类型。例如,以下 sn-p 执行 vector*vector(内积)、vector*scalar 或 scalar*vector 的标准数学定义。具体来说,假设我有一个 Vector<int> 并且标量是 double 类型——我想从“vector*scalar”operator+ 函数返回一个双精度值。

我很确定我缺少一些用于友元函数的 template<class T> 语句(?),但这个 sn-p 并不意味着编译。

template<class T>
class Vector
{
private:
    std::vector<T> base;

public:
    friend Vector operator*(const Vector& lhs, const Vector& rhs);  // inner product    
    Vector<T> operator*(const T scalar);  // vector*scalar   
    friend Vector operator*(const T scalar, const Vector& rhs);  // scalar*vector
};

template<class T>
Vector<T> operator*(const Vector<T>& lhs, const Vector<T>& rhs)  // inner product
{
    assert( lhs.base.size() == rhs.base.size() );

    Vector result;
    result.base.reserve(lhs.base.size());
    std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );

    return result;
}

template<class T>
Vector<T> Vector<T>::operator*(const T scalar)  // vector*scalar
{
    Vector result;
    result.base.reserve(base.size());

    std::transform( base.begin(), base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );

    return result;
}

template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs)  // scalar*vector
{
    Vector result;
    result.base.reserve(rhs.base.size());

    std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );

    return result;
}

【问题讨论】:

  • 什么是编译器错误信息?
  • 我猜你想要的是返回一个向量,其值类型是每个组件操作返回的类型,即int * double 的结果,在这个例子中是double 而不是@ 987654329@。 decltype 可以做到这一点。

标签: c++ templates vector


【解决方案1】:

我猜你想要的是返回一个向量,其值类型是每个组件操作返回的类型,它不一定是标量的类型。

例如:

Vector<int>    * int    -> Vector<int>
Vector<int>    * double -> Vector<double>
Vector<double> * int    -> Vector<double>
Vector<char>   * float  -> Vector<float>

等等

为此,您应该分别定义两种输入类型,例如T1T2(其中一个或两个操作数是它的Vector)。您不想简单地使用标量类型(用于向量 * 标量或标量 * 向量运算)作为结果,否则它可能会被转换(参见我的第三个示例:结果将是 Vector&lt;int&gt;

以上可以使用decltype找到第三个(结果)类型。

为简单起见,将操作符定义为非成员:

template<typename T1, typename T2, typename T3 = decltype(std::declval<T1>() * std::declval<T2>())>
Vector<T3> operator*(const Vector<T1>& lhs, const T2 & scalar)  // inner product
{
    Vector<T3> result;
    //...
    return result;
}

有趣的是

T3 = decltype(std::declval<T1>() * std::declval<T2>())

在这里,我们使用其他两个类型 T1T2 找到类型 T3。首先,我们构造两个值不重要的值(std::declval 函数是一个帮助函数,返回作为模板参数给出的类型)。然后我们将这些值相乘,但结果还是不重要的;我们只对类型感兴趣。这就是第三部分的作用:decltype 为您提供表达式的类型(不计算它)。

其他运算符可以类似地实现。

为了使这些运算符成为朋友,您需要语法

template<...> friend ...

如在Danvil's answer 中看到的那样。

【讨论】:

  • 谢谢!内联声明这种类型的东西是否被认为是一种好习惯? (无论如何,我认为这就是你所拥有的)
  • 另外,我注意到在我的代码中,在operator+ 定义中,我从不为Vector result 声明类型,例如我没有写Vector&lt;T&gt; result,但这是来自作品的原始代码。类型是否被隐式分配?
  • 关于您的第一条评论:在这种情况下,我不知道您所说的“内联”是什么意思。运算符定义的代码应该放在标题中,并且始终是内联的,因为它是一个模板,就像你的整个类的其余部分一样。如果您首先声明,然后定义它(在同一个头文件中)或同时做这两件事,这并不重要。关于您的第二条评论:对于成员函数,您可以写Vector 而不是Vector&lt;T&gt;,它是等价的。但是对于非会员来说,这不起作用,你必须像我的代码 sn-p 一样明确。
  • 再次感谢。当我尝试使用您的想法进行编译时,我遇到了一个错误,但这可能与我在类声明中声明函数而不是定义的方式有关。我声明了一个像template&lt;class T1, class T2, class T3 = decltype(std::declval&lt;T1&gt;() + std::declval&lt;T2&gt;())&gt; friend Vector&lt;T3&gt; operator+(const Vector&lt;T1&gt;&amp; lhs, const Vector&lt;T2&gt;&amp; rhs); 这样的函数,它给出了错误“默认模板参数只允许在类模板上”。有什么想法吗?
  • 哦,我刚刚看到您使用std::multiplies&lt;T&gt;,它计算T * T 为相同的T!你不能为此使用这个函子。您可以使用 lambda。此外,当标量在右侧时,您应该将标量绑定到第二个操作数(或者在切换到 lambda 时相应地编写 lambda)。我会发布更多代码来演示这一点。
【解决方案2】:

朋友声明应该是这样的:

template<class S>
class Vector
{
private:
    std::vector<S> base;

public:
    template<class T> friend T operator*(const Vector<T>& lhs, const Vector<T>& rhs);  // inner product    
    template<class T> friend Vector<T> operator*(const T scalar);  // vector*scalar   
    template<class T> friend Vector<T> operator*(const T scalar, const Vector<T>& rhs);  // scalar*vector
};

其余代码如下:

template<class T>
T operator*(const Vector<T>& lhs, const Vector<T>& rhs)  // inner product
{
    assert( lhs.base.size() == rhs.base.size() );

    Vector<T> result;
    result.base.reserve(lhs.base.size());
    std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );

    return result;
}

template<class T>
Vector<T> operator*(const Vector<T>& lhs, const T scalar)  // vector*scalar
{
    return scalar*lhs;
}

template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs)  // scalar*vector
{
    Vector<T> result;
    result.base.reserve(rhs.base.size());

    std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );

    return result;
}

【讨论】:

  • 谢谢。将第二个“向量*标量”声明为朋友是否有好处,即使它会在类的对象上调用?
【解决方案3】:

您可以像这样添加更多模板参数:

template<class T, class S>
Vector<S> Vector<T>::operator*(const S scalar)

并确保在函数体中的正确位置使用S 标量类型和T 向量元素类型。

【讨论】:

  • 谢谢,但是有没有办法让结果总是两种数据类型中的“较高”,无论是向量还是标量?看起来这只会返回标量对象的数据类型,但是如果向量是双精度而标量是整数呢?
  • @David 是的,看我的回答。
猜你喜欢
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-15
相关资源
最近更新 更多