【问题标题】:How to define a friend template function of a template class outside of its declaration?如何在其声明之外定义模板类的友元模板函数?
【发布时间】:2012-10-13 17:04:13
【问题描述】:

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

#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&lt;&gt;的声明之后,如下,就无法编译了。

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()呢?

提前致谢。

【问题讨论】:

  • 我可以完成这项工作的唯一方法是使用封装 doStuff() 的朋友类。

标签: c++ templates friend


【解决方案1】:

大概是这样的:

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

【讨论】:

  • 我知道这行得通。但我希望doStuff() 使用与类模板相同的模板参数(例如T)。
  • @wbb,它会......!那是因为模板参数推导会弄清楚并基本上将参数的T设置为参数的U。可以这样想:你用一些 X 类型的对象调用 doStuff。除了使用正确的 Q 还能做什么?
  • @wbb Johan 说的,会的。如果您尝试强制使用其他选项,例如 doStuff&lt;double, double&gt;(x, 1);,则会出现错误。
【解决方案2】:

@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&lt;T&gt;的函数,对于任何需要的@987654325 @,其中第二个参数是模板参数。 注意,第一个参数不是模板函数参数。

没有办法一般地说'每当用户使用X&lt;T&gt;类型的类时,使用这个特定的模式来定义 一个函数 doStuff,其中X&lt;T&gt; 是第一个参数。

所以,在你承诺它会存在的类中,但没有办法以模板化的方式编写定义。它必须是这样的

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

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

但是,您可以为任何必需的 T 定义 X&lt;T&gt;(正如您在声明中所承诺的那样),但您必须为每种必需的类型自己做:

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

编辑,回答下面的评论。

确实,例如,所有doStuff(X&lt;U&gt;,int) 都是任何X&lt;T&gt; 的朋友,无论T 和U 是什么类型。这意味着在 doStuff 的主体中,您可以访问 X&lt;Q&gt; 类型的对象的私有信息。在这种情况下,您没有将任何此类对象传递给函数,但您可以在其中创建一个 X&lt;std::string&gt; 并使用它的私有对象。这是您为不使用最初的内联而付出的代价。

【讨论】:

  • 但你的回答使X&lt;T&gt; 授予doStuff&lt;U, V&gt; 友谊,即使T 与U 的类型不同。这不是我的本意。
  • 好吧,确实任何 f(X,int) 都是任何类型 X 的朋友,不管类型 T 和 U。如果 U 和 T 相同,则使用 f 。
猜你喜欢
  • 2013-09-18
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
  • 1970-01-01
  • 2021-02-12
  • 2016-02-24
  • 2016-10-19
相关资源
最近更新 更多