2015-07-13 66 views
0

假设我有具有模板友元函数,希望能实现倍增的阵列(myarray的)值的函数模​​板类的未绑定模板友元函数的返回类型:关于模板类

​​

它适用于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; 
} 

这是不正确的!我想问题是与朋友函数的返回类型。但我不能弄明白。 那么如何在类声明之外定义像这样的朋友模板函数呢?

+1

您有问题吗? – user463035818

+0

模板朋友可能[出了名的困难](https://isocpp.org/wiki/faq/templates#template-friends)得到正确。发布第二个案例的错误和朋友声明。 – TartanLlama

+1

你期望T和大小是什么?你想支持Array是什么?也许你只想让Array成为另一个myArray ,在这种情况下,你应该已经指定了。但是,如果您希望函数的返回独立于输入类型而改变类型,那么您需要指定它(然后不能推导出它)。 – JSF

回答

0

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*scalarscalar*matmat*=scalar

另请注意,矩阵矩阵...只是工作。

这个friend operator是一个好主意的原因是说明here。注意代码不能编译。现在编译为#define ADL to move the operator* into the class as friends。 Koenig运营商让operator*在不作为模板的情况下在template类上运行,并且模板的参数匹配规则很难正确得到,不会过于狭窄,过于宽泛或使用讨厌的SFINAE。

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); 
} 

这是更复杂的,但确实有趣的事情与完美转发(用于缩写C++ 14):

下一步,operator*=可以通过得到改善。一个临时的myArray得到move d到返回值(它允许参考寿命延长工作),而非临时myArray返回一个参考。

这提供了一个原因,即使*=应该是friend。它允许r和lvalues都以相同的方法实现。

另一方面,可能你不希望*=与rvalues一起使用。在这种情况下,请使用带有&参考类别限定符的常规方法来消除该选择。

+1

不需要'operator * ='是一个免费的朋友函数,它可以而且应该只是一个普通的成员函数。除非你决定不公开operator * =',否则'operator *'不需要成为朋友。 –

+0

@ArneMertz我把这种技术称为Koenig操作员。 [this](http://coliru.stacked-crooked.com/a/cb242df537746ab6)编译,而[this](http://coliru.stacked-crooked。com/a/d9dfd0e3bd32b6c7)没有,唯一的区别是我们有一个Koenig'friend operator *'的情况,另一种情况是我们有一个免费的'template operator *'。有好的理由使用'模板'类型的'朋友操作符':模板函数往往被指定不足或超出规定或有令人讨厌的SFINAE要求。 '操作符* ='可能是一个古老的方法,但是完美的转发自我也是很有用的。 – Yakk

+0

@Yakk我不能让你的第一个例子工作。在下面的示例中,您使用非模板友元函数提供了一个很好的解决方案,但是,在定义外部函数时也存在问题。使用模板友元函数,我通过将声明更改为'template friend array操作符*(const Val&lhs,const Array&rhs)'并在body中定义'mat_t'键入'Array'。但是如何在外面定义你的非模板友元函数? – Shindou

0

Tsize是模板参数myArray。如果你定义了一个包含这些模板参数的函数,而不是定义myArray,那么你必须再次声明它们是模板参数,例如,

template <typename T, int size, typename Val, typename Array> 

不过,这不会帮助你多少,即使你得到它的编译,因为你将不能够真正正确地调用操作,因为编译器无法推断出模板参数Tsize。呼叫将不得不尴尬的东西像

auto a = operator*<double, 4>(22, someArray); 

你可能想使一个函数,只返回相同类型作为其第二个参数 - 别的就没有太大的意义反正。为了避免与模板的朋友声明困难(难以得到正确的),你与朋友们的紧耦合,可以中继运营商到公共乘法功能,或者更好的是,运营商* =

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*=应该是会员功能而不是免费的朋友功能请参阅this link

+0

你能演示一个隐式类型转换为一个'myArray&'的例子,它将它绑定到一个临时的目录并导致一个'friend operator * ='的问题?在它是一个左值并且只能通过Koenig查找访问的要求之间,似乎不可能。 – Yakk

+0

@Yakk参见[这里](http://coliru.stacked-crooked.com/a/b59b9a50abf8493d)前面的例子转换可能会导致'operator * =' –

+0

的意外调用Y case没有区别成员或不。 [而'Z'的情况不适用于'朋友操作符* ='(Koenig风格)。](http://coliru.stacked-crooked.com/a/fa48beae40d6a2b1)。那么为什么更喜欢成员'* ='而不是Koenig风格的'friend * ='? – Yakk