实现模板操作员非好友
Implement template operator non-friend
我有一个简单的泛型算术向量类,想要实现标量乘法的*运算符:
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<int,N>我只想给*运算符一个整数值。我进一步希望*算子是可交换的。这就是为什么我不把它实现为一个简单的成员函数,其中左手操作符的类型总是VectorT。
如何在这里实现*操作符作为非成员和非好友?
查看操作员对的定义
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类型的参数调用的。因此Value_T一次是double,另一次是int。您可以乘以2.0来解决歧义,或者在运算符*定义中添加第三个模板参数:
template<class Value_T, unsigned int N, class ScalarType>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, ScalarType s)
到目前为止发布的答案建议为函数参数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
仍然会有正确的类型,就像你希望的那样
上面的方法利用了这样一个事实,即类模板已经提供了一个方便的内部typename 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
首先,您可以使用std::array
而不是实现VectorT
。然而,您遇到的问题是由于表达式Vec3 v2 = v * 2;
中的积分文字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;
}
现场演示
- 有没有可能有一个只有ADL才能找到的非好友功能
- <<操作员在下面的行中工作
- C++ 与操作员不匹配<<
- 为什么我的好友类无法访问私人会员?
- 操作员C++的模棱两可的过载
- C++中>>操作员过载时出现问题?
- 为什么派生类的好友不能使用受保护的成员?
- NaN 上的宇宙飞船操作员
- 解析模板的好友功能时出现问题
- 消除好友和成员二进制运算符的歧义
- 特定好友功能专业化
- 比根<操作员
- 好友函数的工作原理
- SFINAE不能防止模棱两可的操作员过载吗?
- 什么是现实中的"endl"(或任何输出操纵器)?它是如何实现的,它如何与操作员<<一起工
- C++11 中好友模板类的视觉C++错误
- 好友功能 - 成员无法访问
- 为什么"delete"操作员给我访问权限冲突
- 模板好友操作员新明W32
- 实现模板操作员非好友