草庐IT

c++ - 为模板类重载运算符<<

coder 2024-02-07 原文

我正在尝试为返回流的二叉树实现一个方法。我想使用方法中返回的流在屏幕上显示树或将树保存在文件中:

这两个方法在二叉树的类中:

声明:

void streamIND(ostream&,const BinaryTree<T>*);
friend ostream& operator<<(ostream&,const BinaryTree<T>&);

template <class T>
ostream& operator<<(ostream& os,const BinaryTree<T>& tree) {
    streamIND(os,tree.root);
    return os;
}

template <class T>
void streamIND(ostream& os,Node<T> *nb) {
    if (!nb) return;
    if (nb->getLeft()) streamIND(nb->getLeft());
    os << nb->getValue() << " ";
    if (nb->getRight()) streamIND(nb->getRight());
}

此方法在 UsingTree 类中:

void UsingTree::saveToFile(char* file = "table") {
    ofstream f;
    f.open(file,ios::out);
    f << tree;
    f.close();
}

所以我重载了 BinaryTree 类的运算符“<”以使用:cout>< tree="" 和="" ofstream="" f="">< tree,但我收到下一条错误消息:undefined="" reference="" to=""><(std::basic_ostream>&, 二叉树&)'

附言树存储 Word 对象(带有 int 的字符串)。

我希望你能理解我糟糕的英语。谢谢! 我想知道一本适合初学者的关于 STL 的好书,它解释了所有必要的内容,因为我把所有的时间都浪费在了这样的错误上。

编辑:声明 saveToFile() 中的树:BinaryTree< word=""> 树。

最佳答案

问题是编译器没有尝试使用模板化的 operator<<你提供的,而是一个非模板版本。

当您在类中声明一个友元时,您是在封闭范围内注入(inject)该函数的声明。以下代码具有声明(而不是定义)一个采用 non_template_test 的自由函数的效果。通过常量引用论证:

class non_template_test
{
   friend void f( non_template_test const & );
};
// declares here:
// void f( non_template_test const & ); 

模板类也会发生同样的情况,即使在这种情况下它不太直观。当您在模板类主体中声明(而不是定义)一个友元函数时,您是在声明一个具有该确切参数的自由函数。请注意,您声明的是函数,而不是模板函数:

template<typename T>
class template_test
{
    friend void f( template_test<T> const & t );
};
// for each instantiating type T (int, double...) declares:
// void f( template_test<int> const & );
// void f( template_test<double> const & );

int main() {
    template_test<int> t1;
    template_test<double> t2;
}

那些自由函数被声明但没有被定义。这里棘手的部分是那些自由函数不是模板,而是声明的常规自由函数。当您将模板函数添加到组合中时,您会得到:

template<typename T> class template_test {
   friend void f( template_test<T> const & );
};
// when instantiated with int, implicitly declares:
// void f( template_test<int> const & );

template <typename T>
void f( template_test<T> const & x ) {} // 1

int main() {
   template_test<int> t1;
   f( t1 );
}

当编译器命中主函数时,它会实例化模板 template_test与类型 int并且声明了自由函数 void f( template_test<int> const & )那不是模板化的。当它发现调用 f( t1 )有两个f匹配的符号:非模板f( template_test<int> const & )template_test 时声明(但未定义)被实例化,模板化版本在 1 声明和定义.非模板版本优先,编译器匹配它。

当链接器尝试解析 f 的非模板版本时它找不到符号,因此失败。

我们能做什么?有两种不同的解决方案。在第一种情况下,我们让编译器为每个实例化类型提供非模板函数。在第二种情况下,我们将模板化版本声明为友元。它们略有不同,但在大多数情况下是等同的。

让编译器为我们生成非模板函数:

template <typename T>
class test 
{
   friend void f( test<T> const & ) {}
};
// implicitly

这具有根据需要创建尽可能多的非模板化自由函数的效果。当编译器在模板中找到友元声明时 test它不仅找到声明,还找到实现并将两者添加到封闭范围。

使模板化版本成为 friend

要使模板成为友元,我们必须已经声明它并告诉编译器我们想要的友元实际上是一个模板,而不是一个非模板化的自由函数:

template <typename T> class test; // forward declare the template class
template <typename T> void f( test<T> const& ); // forward declare the template
template <typename T>
class test {
   friend void f<>( test<T> const& ); // declare f<T>( test<T> const &) a friend
};
template <typename T> 
void f( test<T> const & ) {}

在这种情况下,在声明 f 之前作为模板,我们必须转发声明模板。申报f模板我们必须先转发声明test模板。友元声明被修改为包含尖括号,用于标识我们要成为友元的元素实际上是模板而不是自由函数。

回到问题

回到您的特定示例,最简单的解决方案是让编译器通过内联友元函数的声明为您生成函数:

template <typename T>
class BinaryTree {
   friend std::ostream& operator<<( std::ostream& o, BinaryTree const & t ) {
      t.dump(o);
      return o;
   }
   void dump( std::ostream& o ) const;
};

使用该代码,您将强制编译器生成一个非模板化的 operator<<对于每个实例化类型,生成的函数委托(delegate) dump模板的方法。

关于c++ - 为模板类重载运算符<<,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1810753/

有关c++ - 为模板类重载运算符<<的更多相关文章

  1. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  4. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  5. ruby-on-rails - Nokogiri:使用 XPath 搜索 <div> - 2

    我使用Nokogiri(Rubygem)css搜索寻找某些在我的html里面。看起来Nokogiri的css搜索不喜欢正则表达式。我想切换到Nokogiri的xpath搜索,因为这似乎支持搜索字符串中的正则表达式。如何在xpath搜索中实现下面提到的(伪)css搜索?require'rubygems'require'nokogiri'value=Nokogiri::HTML.parse(ABBlaCD3"HTML_END#my_blockisgivenmy_bl="1"#my_eqcorrespondstothisregexmy_eq="\/[0-9]+\/"#FIXMEThefoll

  6. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  7. ruby-on-rails - Mandrill API 模板 - 2

    我正在使用Mandrill的RubyAPIGem并使用以下简单的测试模板:testastic按照Heroku指南中的示例,我有以下Ruby代码:require'mandrill'm=Mandrill::API.newrendered=m.templates.render'test-template',[{:header=>'someheadertext',:main_section=>'Themaincontentblock',:footer=>'asdf'}]mail(:to=>"JaysonLane",:subject=>"TestEmail")do|format|format.h

  8. ruby - Chef Ruby 遍历 .erb 模板文件中的属性 - 2

    所以这可能有点令人困惑,但请耐心等待。简而言之,我想遍历具有特定键值的所有属性,然后如果值不为空,则将它们插入到模板中。这是我的代码:属性:#===DefaultfileConfigurations#default['elasticsearch']['default']['ES_USER']=''default['elasticsearch']['default']['ES_GROUP']=''default['elasticsearch']['default']['ES_HEAP_SIZE']=''default['elasticsearch']['default']['MAX_OP

  9. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  10. ruby - 带括号和 splat 运算符的并行赋值 - 2

    我明白了:x,(y,z)=1,*[2,3]x#=>1y#=>2z#=>nil我想知道为什么z的值为nil。 最佳答案 x,(y,z)=1,*[2,3]右侧的splat*是内联扩展的,所以它等同于:x,(y,z)=1,2,3左边带括号的列表被视为嵌套赋值,所以它等价于:x=1y,z=23被丢弃,而z被分配给nil。 关于ruby-带括号和splat运算符的并行赋值,我们在StackOverflow上找到一个类似的问题: https://stackoverflow

随机推荐