如何在声明之外定义模板类的朋友模板函数?

时间:2012-10-13 17:04:14

标签: c++ templates friend

如果我在模板类声明中定义了friend模板函数,如下所示,它可以编译。

#include <iostream>

template <typename T>
class X {
public:
    X(int i) : n(i) {}

private:
    T n;

    template <typename U> 
    friend void doStuff(const X<T>& x, const U& u)
    {
        std::cout << (x.n + u) << std::endl;
    }
};

int main()
{
    X<int> x(1);
    doStuff(x, 3);
    return 0;
}

但是,如果我将doStuff()的定义移出类decalaration,就在声明类X<>之后,如下所示,它就无法编译。

template <typename U>
template <typename T>
void doStuff(const X<T>& x, const U& u)
{
    std::cout << (x.n + u) << std::endl;
}

以下代码也没有。

template <typename U, typename T>
void doStuff(const X<T>& x, const U& u)
{
    std::cout << (x.n + u) << std::endl;
}

那么我应该如何在类声明之外定义模板函数doStuff()

提前致谢。

2 个答案:

答案 0 :(得分:5)

也许是这样:

template <typename T>
class X {
public:
    X(int i) : n(i) {}

private:
    T n;

    template <typename U, typename V>
    friend void doStuff(const X<U>& x, const V& u);
};

template <typename U, typename V>
void doStuff(const X<U>& x, const V& u)
{
    std::cout << (x.n + u) << std::endl;
}

int main()
{
    X<int> x(1);
    doStuff(x, 3);
    X<double> y(1.0);
    doStuff(y, 3.5);
}

答案 1 :(得分:3)

@jrok提供了一个很好的解决方案。

让我们详细说明它的工作原理,以及为什么需要它。

将该函数声明为

template <typename U, typename V>
friend void doStuff(const X<U>& x, const V& u);

你说每个X和V都有一个函数doStuff。但是你问,如果U不是T怎么办?好吧,当你使用类型为X,int的参数调用doStuff时,模板参数推导的唯一可接受的结果是将类定义的类型T设置为int。

现在,为什么这样做不起作用:

template <typename V>
friend void doStuff(const X<T>& x, const V& u);

实际上,它确实如此。但这意味着对于任何类型T,您告诉编译器定义一个带X<T>的函数,任何必需的T,以及第二个参数是模板参数的位置。 请注意,第一个参数不是模板函数参数。

当用户使用类型为X<T>的类时,无法通常说',使用特定模式来了解如何定义< / em>一个函数doStuff,其中X<T>是第一个参数。

所以,在课堂上你承诺它会存在,但是没有办法以模板的方式写出这个定义。它必须是

之类的东西
template <typename T,typename U>
for_any_seen_template_instance_anywhere_in_the_program_< X<T> >:: 
      void doStuff(const X<U>& x, const V& u){
   ... code.
}

没有这样的事情。另一种思考方式:写作时

doStuff(a,b); 

如果第一个参数不是函数模板参数,则不会自动生成与第一个参数相关的任何内容。类模板的模板参数类型不会像模板函数参数那样自动推导出来。

但是,您可以为任何所需的X<T>定义T(正如您在声明中所承诺的那样),但您必须自己为每个必需的类型执行此操作:

template<typename V>
void doStuff(const X<int>& x, const V& u){
   std::cout << (x.n + u) << std::endl;
}

编辑,回答以下评论。

确实,所有doStuff(X<U>,int)都是X<T>的朋友,无论TU是什么类型。这意味着在doStuff体内,您可以访问类型为X<Q>的对象的私有。在这种情况下,您没有将任何此类对象传递到函数中,但您可以在其中创建X<std::string>并使用它的私有。这是您为不使用最初的内联而支付的价格。