我有一个简单的generice算术向量类,想要为标量乘法实现*运算符:
template<class Value_T, unsigned int N>
class VectorT
{
public:
typedef Value_T value_type;
typedef unsigned int size_type;
size_type GetNumElements() const { return N; }
// class member
// elements in the vector
value_type elements[N];
// the number of elements
static const size_type size = N;
};
// scalar multiplication
template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, Value_T s)
{
VectorT<Value_T,N> vTemp(v);
for (unsigned int i = 0; i < v.GetNumElements(); i++)
{
vTemp.elements[i] *= s;
}
return vTemp;
}
像这样使用它......
typedef VectorT<double, 3> Vec3;
int main(int argc, char* argv[])
{
Vec3 v;
Vec3 v2 = v * 2; // multiply with scalar int
return 0;
}
...给编译器错误C2782(MSVC2012)*运算符的模板参数Value_T是不明确的:int或double。
如果我在我的类中将*运算符定义为友元函数,则错误消失,并且对于标量int或double工作正常。但实际上这里没有必要进行朋友声明,因为VectorT类的公共接口对于运营商来说已经足够了(在我的实际代码中我有成员privat和一些公共访问器方法)。
我希望标量乘法仅适用于Value_T类型:对于VectorT&lt; int,N&gt;我只希望给*运算符赋值。我还希望*运算符是commutativ。这就是为什么我不将它作为一个简单的成员函数实现的,其中左手操作符始终是VectorT类型。
如何在这里将*运营商作为非会员和非朋友实施?
答案 0 :(得分:4)
查看运营商的定义
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, Value_T s)
并在线
Vec3 v2 = v * 2; // multiply with scalar int
使用VectorT和int类型的参数调用operator *。所以Value_T是一次加倍,另一次是另一次。您可以乘以2.0来解决歧义,或者将第三个模板参数添加到operator * definition:
template<class Value_T, unsigned int N, class ScalarType>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, ScalarType s)
答案 1 :(得分:3)
到目前为止发布的答案建议为函数参数s
的类型提供独立的模板参数。这是一种可行的方法,但不是唯一的方法。
或者,更好的解决方案可能是通过故意将其放入非推导的上下文
来从模板参数推导过程中排除第二个参数。// scalar multiplication
template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v,
typename VectorT<Value_T, N>::value_type s)
{
...
}
这样编译器将从v
参数的类型中推导出模板参数,但不会从s
参数的类型中推断出。 s
仍然会有正确的类型,就像你想要的那样。
上述方法利用了这样一个事实,即您的类模板已经提供了方便的内部类型名value_type
。在更一般的情况下,可以使用identity
模板
template <typename T> struct identity
{
typedef T type;
};
template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v,
typename identity<Value_T>::type s)
{
...
}
C ++标准库决定不包含std::identity
模板,因为std::decay
和std::remove_reference
显然涵盖了其功能。因此,通过使用标准模板,您可以实现通用解决方案
template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v,
typename std::decay<Value_T>::type s)
{
...
}
以下文章提到了这个具体问题 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3766.html
答案 2 :(得分:2)
首先,您可以使用std::array
代替实施VectorT
。然而,您遇到的问题是由于表达式2
中的整数文字Vec3 v2 = v * 2;
属于int
类型。因此,编译器无法推导出您的重载operator*
,因为它实现时仅适用于VectorT
包含具有乘数的相同类型元素的情况。
为了解决这个问题,您可以为重载的operator*
添加额外的模板参数,如下例所示:
template<class Value_T1, class Value_T2, unsigned int N>
VectorT<Value_T1, N> operator*(const VectorT<Value_T1, N>& v, Value_T2 s)
{
VectorT<Value_T1, N> vTemp(v);
for(unsigned int i(0), sz(v.GetNumElements()); i < sz; ++i) {
vTemp.elements[i] *= s;
}
return vTemp;
}