1

如果我在模板类声明里面定义朋友模板函数,如下,就可以编译了。

#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()移出类声明,就在声明类之后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()在类声明之外定义模板函数呢?

提前致谢。

4

2 回答 2

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);
}
于 2012-10-13T17:30:01.430 回答
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>, 对于任何 required T,并且第二个参数是模板参数。请注意,第一个参数不是模板函数参数。

没有办法笼统地说‘每当用户使用 type 类时X<T>,使用这个特定的模式来定义一个函数 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 的主体内,您可以访问 type 对象的私有信息X<Q>。在这种情况下,您没有将任何此类对象传递给函数,但您可以X<std::string>在其中创建一个并使用它的私有对象。这是您为不使用最初的内联而付出的代价。

于 2012-10-13T18:05:08.903 回答