为什么运营商<<暧昧吗?

时间:2017-03-22 16:10:37

标签: c++ overloading

我在全局范围内将函数模板定义为

.visible

然后

template<class T>
inline std::ostream& operator<<(std::ostream& os, T const& t)
{       
    return os;
}
由于含糊不清,

无法编译。我认为这是函数重载,编译可以解决它,并为std::cout << "\n"; 选择更具体的版本。为什么不编译?

2 个答案:

答案 0 :(得分:3)

您的operator<<版本在一个参数上是模板化的,标准提供的版本也是如此。

问题在于你的版本(我们称之为函数 a )是在一个参数上模板化的:

template<class T>
inline std::ostream& operator<<(std::ostream& os, T const& t)
{       
    return os;
}

cppreference开始,您可以看到const char*(我们称之为 b )的标准重载在另一方面是模板化的:

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  
                                        const char* s );

您可以通过使operator<<版本为第一个参数使用相同的模板参数(这个是 c )来获得您期望的行为:

template<class T, class CharT, class Traits>
inline std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, T const& t)
{       
        return os;
}

快乐地编译,正如您所见on coliru

这是因为在选择具有多个候选模板函数的重载时,模板重载选择会选择最专业的模板。

具体来说,首先转换模板:

[temp.func.order]:

  
      
  1. 通过依次转换每个模板(参见下一段)并使用函数类型执行模板参数推导,部分排序选择两个函数模板中哪一个比另一个更专业。演绎过程确定其中一个模板是否比另一个模板更专业。如果是这样,则更专业的模板是部分订购流程选择的模板。

  2.   
  3. 要生成转换模板,对于每种类型,非类型或模板模板参数(包括其模板参数包)分别合成唯一类型,值或类模板,并将其替换为每次出现模板的函数类型中的参数。 [注意:在非类型模板参数的合成值类型中替换占位符的类型也是唯一的合成类型。 - 尾注] ......

  4.   

[temp.deduct.partial]:

  
      
  1. 使用两组类型来确定部分排序。对于涉及的每个模板,都有原始函数类型和转换后的函数类型。 [注意:[temp.func.order]中描述了转换类型的创建。 - 结束注释] 演绎过程使用变换后的类型作为参数模板,将另一个模板的原始类型用作参数模板。对于部分排序比较中涉及的每种类型,此过程完成两次:一次使用转换的模板-1作为参数模板,使用template-2作为参数模板,再次使用转换的模板-2作为参数模板和模板-1作为参数模板。

  2.   
  3. 用于确定排序的类型取决于完成部分排序的上下文:

         
        
    1. 在函数调用的上下文中,使用的类型是函数调用具有参数的函数参数类型。
    2.   
  4.         

    ...

         
        
    1. 功能模板F至少与功能模板G一样专用,如果对于用于确定排序的每对类型,F的类型至少与G中的类型一样专用.F比G更专业F至少与G一样专业,G至少不像F那样专业。
    2.   

因此,我们希望模板中的部分是函数参数。

a) T                 =>  std::ostream&,                     T const&
b) CharT, Traits     =>  std::basic_ostream<CharT, Traits>, const char*
c) T, CharT, Traits  =>  std::basic_ostream<CharT, Traits>, T const&

对于这些函数的第一个参数, a b c 更专业。

对于这些函数的第二个参数, b a c 更专业。

正如我们在[temp.deduct.partial] / 10中学到的那样,参数推导要求函数 f1 的所有相关参数至少与函数 f2的所有参数一样专用 f1 函数的至少与 f2 一样专业, a 在这里不能比 b <更专业/ em>,因为每个参数都有一个比另一个匹配参数更专业的参数。

c 不是这种情况,因为 a c 的所有参数都至少与< EM> C

因此,在 a b 之间进行选择时,重载决策将不明确,因为 a 至少与 b <一样专业/ em>和 b 至少与 a 一样专业。使用 c 而不是 a 可以消除歧义,因为 b 至少与 c 一样专业,但是反之则不然。

正如我们从[temp.func.order] / 2中所知,更专业的函数赢得了重载解析,并且使用 c 而不是 a ,获胜者是 b ,而行std::cout << "hello";hello打印到控制台。

答案 1 :(得分:0)

有一个功能模板

template <class Traits>
... operator<<(basic_ostream<char, Traits> &, const char *); // @1

在标准库中声明。

替换后,需要转换标准专业化和你的专业化,a.k.a。

template<class T>
... operator<<(std::ostream& os, T const& t); // @2

都被列为完全匹配

然后编译器尝试通过partial ordering rules解决此调用:

@1 from transformed @2 :
(basic_ostream<char, T> &, const char *) from (std::ostream& os, U const& t)
: P2 = const char *, A2 = U const & : deduction fails

@2 from transformed @1 :
(std::ostream& os, T const& t) from (basic_ostream<char, U> &, const char *)
: P1 = std::ostream&, A1 = basic_ostream<char, U> & : deduction fails

因此两者都不是更专业,呼叫是模棱两可的。

还有一些其他候选人:

operator<<(basic_ostream<_CharT, _Traits>&, const char*)
operator<<(basic_ostream<_CharT, _Traits>&, const _CharT*)

但遗憾的是,在这种情况下,他们两人的专业性都不如大哥@ 1。所以我踢了他们......

还有一些,但这两个只是为了使错误信息的可读性降低。

operator<<(bool __n)
operator<<(const void* __p)