关于模板类的未绑定模板友元函数的返回类型
About the return type of an unbound template friend function of a template class
假设我有一个模板类,它有一个模板友函数,希望实现一个值乘以数组(myArray)的函数:
template<typename T, int size>
class myArray{
T *_array;
public:
...
template<typename Val, typename Array>
friend myArray<T,size> operator*(const Val &lhs, const Array &rhs){
myArray<T,size> mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
...
};
它在VS2013上工作。然后将未绑定模板友元函数的定义移到外面:
template<typename Val, typename Array>
myArray<T,size> operator*(const Val &lhs, const Array &rhs){
myArray<T,size> mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
是不正确的!我想问题出在朋友函数的返回类型上。但我不可能不明白。那么如何在类声明之外定义像这样的友元模板函数呢?
像第一个例子那样声明的friend
函数是奇怪的野兽。myArray<T,Size>
的每个实例创建一个不同的 friend
函数,该函数只能通过参数依赖查找在myArray<T,Size>
上找到。
没有这样声明的自由operator*
不能这样工作。
你可以让你的代码这样工作:
template<class Val, class T, int size>
myArray<T,size> operator*(const Val &lhs, const myArray<T,size> &rhs){
myArray<T,size> mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
这里所有的模板形参都列在template<>
列表中,它们都可以从*
的参数中推导出来。
请记住将operator*
与myArray
放在同一个命名空间中。
然而,我个人认为:
friend myArray operator*(const T&lhs, const myArray &rhs){
myArray mat_t;
for(int i = 0;i < size; i++)
mat_t._array[i] = lhs * rhs._array[i];
return mat_t;
}
一个非template
的朋友operator*
。同样,其中一个是为每个myArray<T,size>
"衍生"的,但它本身不是template
。这有一定的优点,比如它在转换构造函数中表现得更好。
更进一步,我们得到:
friend myArray& operator*=(myArray&mat, const T&scalar){
for(int i = 0;i < size; i++)
mat._array[i] *= scalar;
return mat;
}
friend myArray operator*(const T&scalar, myArray mat){
mat *= scalar;
return mat;
}
friend myArray operator*(myArray mat, const T&scalar){
mat *= scalar;
return mat;
}
,我们首先创建一个*=
,然后用它写*
。请注意,*
按值接受myArray
(因为无论如何我都需要返回一个副本,不妨让它发生在方法之外),并且我同时支持mat*scalar
、scalar*mat
和mat*=scalar
。
还要注意矩阵的矩阵…只是工作。
为什么这个friend operator
是一个好主意在这里说明。注意,代码不能编译。现在#define ADL
将operator*
作为friends
移动到类中并进行编译。Koenig操作符允许operator*
在没有模板的情况下对template
类进行操作,而模板的参数匹配规则如果不太窄、太宽或使用令人不快的SFINAE,就很难得到正确的匹配规则。
下一步,operator*=
可以通过以下方式改进:(c++ 14为简洁使用)
template<class Array
class=std::enable_if_t<std::is_same<std::decay_t<Array>,myArray>>
>
friend Array operator*=(Array&&mat, const T&scalar){
for(int i = 0;i < size; i++)
mat._array[i] *= scalar;
return std::forward<Array>(mat);
}
更复杂,但通过完美的转发做了有趣的事情。临时myArray
将move
d作为返回值(允许引用生命周期扩展工作),而非临时myArray
返回引用。
这提供了为什么*=
也应该是friend
的原因。它允许在同一个方法中实现r值和左值。
另一方面,可能您不希望*=
处理右值。在这种情况下,使用带有&
引用类别限定符的常规方法来消除这种选择。
T
和size
为myArray
的模板参数。如果你把包含这些模板参数的函数的定义放在myArray
的定义之外,你必须再次声明,它们是模板参数,例如
template <typename T, int size, typename Val, typename Array>
然而,即使你编译了它,这对你也没有多大帮助,因为你不能正确地调用操作符,因为编译器不能推断出模板参数T
和size
。调用必须是一些尴尬的东西,比如
auto a = operator*<double, 4>(22, someArray);
你可能想让一个函数只返回与它的第二个参数相同的类型——其他任何东西都没有多大意义。为了避免在模板友元声明中遇到的困难(很难正确处理)以及与友元之间的紧密耦合,可以将操作符传递给公共乘法函数,或者更好的做法是,operator*=
template<typename T, int size>
class myArray {
T *_array;
public:
myArray() : _array(new T[size]{22}) {}
//...
template<typename Val>
myArray& operator*=(const Val &val) {
for (int i = 0; i < size; ++i)
_array[i] *= val ;
return *this;
}
};
template <typename Val, typename T, int size>
myArray<T,size> operator*(const Val &lhs, myArray<T, size> tmp) {
tmp *= lhs;
return tmp;
}
关于为什么operator*=
应该是成员函数而不是免费友元函数的基本原理,请参阅此链接
- C++模板来检查友元函数的存在
- 如何使用单独文件中的派生类访问友元函数对象
- 模板化的类和友元函数
- 友元函数无法访问私有数据成员 (c++)
- 继承和友元函数,从基类访问受保护的成员
- 如何在友元函数中使用静态成员而不添加前缀 [类名]::
- 在模板类之外定义友元函数的正确方法是什么?
- 2个模板化类的非模板友元函数未定义引用错误
- C++ 友元函数和私有构造函数
- 使第二个类的构造函数成为第一个类中的友元函数
- 未定义的类模板不会实例化以检查友元函数
- C++类中的友元函数有问题?
- 使用typedef'ed返回类型声明友元函数时出现编译器错误
- 模板类中的模板友元函数
- C++ 17 个友元函数声明和内联命名空间
- 友元函数需要一个帮助程序函数
- 在类内定义的友元函数与类外定义的友元函数的查找规则之间的差异
- 运算符重载在 C++ 中使用友元函数
- 类C++友元函数无法访问封装的类
- 函数指针作为友元函数