【问题标题】:Why is friend member function not recognized as function template automatically?为什么好友成员函数不能自动识别为函数模板?
【发布时间】:2012-08-08 12:02:07
【问题描述】:

c++ 常见问题 35.16

http://www.parashift.com/c++-faq-lite/template-friends.html

#include <iostream>

template<typename T>
class Foo {
public:
  Foo(T const& value = T());
  friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs);
  friend std::ostream& operator<< (std::ostream& o, const Foo<T>& x);
private:
  T value_;
};

作者声称:

'当编译器在类定义中正确地看到友元行时,就会发生障碍。那时它还不知道友元函数本身就是模板(为什么?类模板成员函数不是默认的函数模板吗?);它假设它们是这样的非模板:'

Foo<int> operator+ (const Foo<int>& lhs, const Foo<int>& rhs)
{ ... }  

std::ostream& operator<< (std::ostream& o, const Foo<int>& x)
{ ... }

为什么以上不是模板?这些模板不是通过 int 实例化的吗?

'当你调用 operator+ 或 operator

事实上,为了让编译器将上面的内容识别为函数模板,程序员必须像下面这样显式地这样做:

template<typename T> class Foo;  // pre-declare the template class itself
template<typename T> Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs);
template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x);

谁能解释一下?我觉得这很烦人,不知道为什么编译器不只是通过将 T 替换为 'int' 来实例化 Foo 类的实例,然后就结束了。

谢谢。

【问题讨论】:

    标签: c++


    【解决方案1】:

    类模板成员函数是模板的一部分,因此使用模板实例化,但朋友不是。考虑非模板情况:

    struct S {
        friend void foo(S);
    };
    

    注意void foo(S)此时不必声明; friend 声明是说如果定义了void foo(S) 函数,那么该函数将有权访问S。它可能永远不会被真正定义,这很好。

    有了模板,情况也一样:

    template<typename T> struct S {
        friend void foo(S);
    };
    

    这意味着对于任何类型 T如果定义了函数 void foo(S&lt;T&gt;),则该函数可以访问 S&lt;T&gt;。通过重载,该函数应该是一个具体的函数:

    void foo(S<char>) { }
    void foo(S<int>) { }
    

    编译器不知道您计划稍后提供可用于所有T 的函数模板。相反,如果已经声明了适当的函数模板,那么如果您通过添加尖括号指定它应该被实例化,它将被实例化。

    至于为什么必须前向声明模板,“模板”没有理由必须只有一个声明。考虑:

    #include <iostream>
    template<typename T> struct S;
    template<typename T> void foo(S<T>);
    template<typename T> void foo(S<T *>);
    template<typename T> struct S {
        friend void foo<>(S);
    };
    template<typename T> void foo(S<T>) { std::cout << "template template friend\n"; }
    template<typename T> void foo(S<T *>) { std::cout << "template specialization template friend\n"; }
    template void foo(S<void *>);
    int main() {
        foo(S<int>());
        foo(S<void *>());
    }
    

    这里foo 有两个特化,它们都必须前向声明,以便friend 可以在它们之间进行选择。

    【讨论】:

    • 我了解一些,但不是全部。 With 'friend Foo operator+ (const Foo& lhs, const Foo& rhs);'沿着,编译器会看到什么?在找到 operator+ 的前向声明之前,operator+ 将是 Foo 的友元函数,返回类型、函数参数等仅此而已?
    • @user1559625 编译器看到,对于任何特定的T,如果定义了Foo&lt;T&gt; operator+ (const Foo&lt;T&gt;&amp; lhs, const Foo&lt;T&gt;&amp; rhs);,它将成为朋友。此外,如果已经声明了适当的模板,那么它将被实例化。
    • 更正;指定实例化模板的语法不同(额外的尖括号)。
    猜你喜欢
    • 1970-01-01
    • 2015-09-04
    • 2015-01-13
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    • 2019-05-20
    相关资源
    最近更新 更多