繁体   English   中英

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

[英]How to define a friend template function of a template class outside of its declaration?

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

#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()

提前致谢。

也许像这样:

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

@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都告诉编译器要为任何必需的T 定义一个采用X<T>的函数,并且第二个参数是模板参数。 请注意,第一个参数不是模板函数参数。

没有通用的说法:“每当用户使用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); 

如果第一个参数不是函数模板参数,则不会自动生成该第一个参数的任何内容。 类模板的模板参数类型不会像模板功能参数一样自动推导。

但是,您可以为任何必需的T定义X<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>并使用它的私有函数。 这是您为不使用原始内联而支付的价格。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM