【问题标题】:How do you declare the friend of a generic class outside of the class decleration?如何在类声明之外声明泛型类的朋友?
【发布时间】:2012-03-17 23:11:01
【问题描述】:

以下代码有效,但我想将 ostream& operator

#include<iostream>
#include<map>

using namespace std;

template <class T>
class hash {
  private:
    map<string, T> _map;
  public:
    T& operator[] (string x);
    friend ostream& operator<<(ostream& out, const hash<T> &rhs) { return out << "test"; }
};

template <class T>
T & hash<T>::operator[](string x) {
  return _map[x];
}

int main () {
  hash<int> myobject;
  myobject["a"] = 1;
  cout << myobject["a"] << endl;
  cout << myobject << endl;
  return 0;
}

我试过了:

template <class T>
ostream& operator<<(...) {
  return out << "test";
}

ostream& operator<<(...) {
  return out << "test";
}

以及其他一些组合都无济于事。

【问题讨论】:

标签: c++ templates generics friend


【解决方案1】:

由于这个问题似乎并没有因为完全重复而被关闭,所以我将解释你的程序是做什么的。

template <typename T>
class test {
   int private_field;
   friend std::ostream& operator<<( std::ostream&, test<T> const & );
};
// ???
int main() {
   std::cout << test<int>() << std::endl;
}

模板是按需实例化的(除非你明确地实例化它们),这意味着在这个特定的程序中test 仅实例化为test&lt;int&gt;。当编译器实例化模板时(因为它是在main 中请求的),它将处理模板定义。此时的行为类似于在定义点用替换类型重写代码(在这种情况下,在 main 之前):

class test<int> {
   friend std::ostream& operator<<( std::ostream&, test<int> const & );
};

现在,如果您查看实例化模板,您会注意到 friend 声明与非模板化函数成为朋友。因此,在这个特定程序中,您可以使用该特定功能填写 ???

std::ostream& operator<<( std::ostream& o, test<int> const & t ) {
   return o << t.private_field;
}

这里的问题是它不容易扩展。 operator&lt;&lt; 的代码对于 test&lt;int&gt;test&lt;double&gt; 的代码相同,因此不需要重写所有实例化类型的相同函数!

此时有两个选项,如您已经确定的,第一个是在类中提供函数的定义。每当实例化类型时,编译器就会处理和定义函数。此解决方案根据需要为每个模板实例化创建非模板化函数(这是我会做的选择,即使在这里查找的怪癖和奇怪之处也是如此)。

现在,如果您真的想避免在模板类中提供定义,并且仍然想提供单个实现,那么您必须求助于提供模板operator&lt;&lt;。此时您有两种不同的选择,您可以声明模板的 all 实例化(我不太喜欢,因为它向太多其他人开放),或者您可以与模板函数(访问更简洁,写起来更麻烦)。

第一种情况是:

template <typename T>
class test {
   template <typename U>
   friend std::ostream& operator<<( std::ostream&, test<U> const & );
};
template <typename T>
std::ostream& operator<<( std::ostream&, test<T> const & ) { ... }

第二种情况需要几个前向声明:

template <typename T> test;
template <typename T> std::ostream& operator<<( std::ostream&, test<T> const & );
template <typename T>
class test {
   friend std::ostream& operator<< <T>( std::ostream&, const test<T>& );
};
template <typename T>
std::ostream& operator<<( std::ostream&, test<T> const & ) { ... }

当然还有另一种选择:根本不声明friends。在具有实现的类中提供 print(std::ostream&amp;) 公共函数,并提供仅在第二个参数上调用 print 的非模板化 operator&lt;&lt;

【讨论】:

    【解决方案2】:

    使用朋友模板

    要小心
    #include<iostream>
    #include<map>
    
    template <class T>
    class MyHash{
    public:
        // ... use your T template here
        template <class U>
        friend ostream& operator<<(ostream& out, const MyHash<U> &rhs);
    };
    
    // ...
    template <class U>
    ostream& operator<<(ostream& out, const MyHash<U> &rhs){ return out << "test"; }
    

    你需要使用不同的 template U 因为 operator 是 friend 方法(外部),如果我们使用 T 而不是 U,我们将有:模板中的模糊定义

    通过 MyHash 更改 hash 以避免歧义。

    编辑:

    你得到here的错误是:

    错误 C2676: '

    因为您忘记在 "hash.h" 中包含 &lt;string&gt;。该标头定义

    还可以尝试将 operator[]operator 的定义直接移动到 "hash.h" 必须包括模板的声明和定义。这是因为在某些编译器中,模板函数无法独立编译和链接,因为它们是根据实例化特定类型的请求生成的。

    #include "hash.cpp" 更改为 #include "hash.h"

    【讨论】:

    • 我有一个后续问题:当我使用您的解决方案时,我将函数定义放在与类声明不同的文件中,我需要在定义中包含 .cpp 文件,而不是.h 文件,当我尝试使用该类时。我做错了什么?
    • here 就是我所说的一个例子。如果我要按原样编译它,一切都很好,但是如果我将 test.cpp 中的 include hash.cpp 更改为 hash.h,并使用 g++ test.cpp hash.cpp 进行编译,则它不会编译
    • 在“hash.h”中简要包含 ,或阅读我的答案中的编辑部分以了解更多详细信息。
    猜你喜欢
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    相关资源
    最近更新 更多