设为首页 加入收藏

TOP

Effective C++ 条款 46:需要类型转换时请为模板定义非成员函数
2015-07-20 17:37:29 来源: 作者: 【 】 浏览:3
Tags:Effective 条款 需要 类型 转换 时请为 模板 定义 成员 函数

(一)

首先我们回顾条款24,它说过为什么惟有non-member函数才有能力“在所有实参身上实施隐式类型转换”。

现在我们将Rational和operator*模板化:

template
  
   
class Rational {
public:
	Rational(const T& number = 0, const T& denominator = 1);
	const T number() const;
	const T denominator() const;
};

template
   
     const Rational
    
      operator* (const Rational
     
      & lhs, const Rational
      
       & rhs) { ... }
       许多编译器实质上会强迫你把所有template定义式放进头文件内,所以你或许需要在头文件内定义doMultiply(条款30,这样的templates不需非得是inline不可),看起来像这样:
      
     
    
   
  
我们希望以上代码支持混合算术运算,所以我们希望以下代码能够顺利通过编译:
Rational
  
    oneHalf(1,2); 
Rational
   
     result = oneHalf * 2;//错误!无法通过编译
   
  

条款24内,编译器知道我们尝试调用什么函数(就是接受两个Rationals参数的那个operator*),但这里编译器不知道我们想要调用哪个函数。取而代之的是,他们试图想出什么函数被名为operator*的template具现化(产生)出来。它们知道应该可以具现化某个“名为operator*并接受两个Rational 参数”的函数,但完成这一具现化行动,必须先算出T是什么。问题是它们没这个能耐。


(二)

解决方法:template class内的friend声明式可以指涉某个特定函数。

class template并不依赖template实参推导(后者只施行于function template身上),所以编译器总是能在class Rational 具现化时得知T,所以令Rational class 声明适当的operator*为其friend函数,可简化整个问题:

template
  
    
class Rational{ 
public: 
    friend 
        const Rational operator* (const Rational& lhs, const Rational& rhs);    //声明operator*函数 
}; 
template
   
     const Rational
    
      operator* (const Rational
     
      & lhs, const Rational
      
       & rhs) {......}
      
     
    
   
  
现在对operator*的混合式调用可以通过编译了。因为当对象oneHalf被声明为一个Rational ,class Rational 于是被具现化出来,而作为过程的一部分,friends函数operator*(接受Rational 参数)也就被自动声明出来。后者身为一个函数而非函数模板,因此编译器可在调用它的时候使用隐式转换函数(例如Rational的non-explicit构造函数)。

(三)

代码改过以后可以通过编译,因为编译器知道我们要调用哪个函数(就是接受一个Rational 以及又一个Rational 的那个operator*),但是却不能成功连接。

不能连接的原因:

那个函数只是被声明于Rational内,并没有被定义出来,我们意图令此class外部的operator* template提供定义式,但是行不通――如果我们自己声明了一个函数(那正是Rational template内的作为),就有责任定义那个函数,既然我们没有提供定义式,连接器当然找不到它。

所以要解决这个问题的方法就是:将operator*函数本体合并至其声明式内:

template
     
      
class Rational {
public:
	friend const Rational operator* (const Rational& lhs, const Rational& rhs)
	{
		return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
	}
};
     
我们虽然使用friend,却与friend的传统用途“访问class的non-public成分”毫不相干。为了让类型转换可能发生在所有参数身上,我们需要一个non-member函数:为了令这个函数被自动具现化,我们需要将它声明在一个class内部:而在class内部声明non-member函数的唯一办法就是:令他成为一个friend。
以上代码就可以顺利通过编译,连接,运行了!


(四)

定义于class内的函数都暗自成为inline,包括operator*这样的friend函数。你可以将这样的inline声明所带来的冲击最小化,做法是令operator*不做任何事情,只调用一个定义于class外部的辅助函数。Rational是个template意味着这个辅助函数通常也是一个template。

许多编译器实质上会强迫你把所有template定义式放进头文件内,所以你或许需要在头文件内定义doMultiply(条款30,这样的templates不需非得是inline不可),Rational头文件代码看起来像这样:

template
     
       class Rational;
template
      
        const Rational
       
         doMultiply (const Rational
        
         & lhs, const Rational
         
          & rhs); //声明helper template template 
          
            class Rational { public: ... friend const Rational
           
             operator* (const Rational
            
             & lhs, const Rational
             
              & rhs) { return doMultiply(lhs, rhs); } }; template
              
                const Rational
               
                 doMultiply (const Rational
                
                 & lhs, const Rational
                 
                  & rhs) { return Rational
                  
                   (lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator()); }
                  
                 
                
               
              
             
            
           
          
         
        
       
      
     

请记住:

(1)当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。






】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇UVA 514 Rails 下一篇SGU 103. Traffic Lights 带限制..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·利用python进行数据 (2025-12-25 20:49:22)
·如何使用 python 中 (2025-12-25 20:49:19)
·零基础如何学爬虫技 (2025-12-25 20:49:17)
·Java 并发工具类:提 (2025-12-25 20:25:44)
·Java面试技巧:如何 (2025-12-25 20:25:41)