ISO C ++标准符合以下代码的结果

时间:2012-02-22 14:12:18

标签: c++ visual-c++ g++ iso icc

#include <iostream>

template< typename U >
struct base {
    template< typename T >
    base const & operator<<( T x ) const {
        std::cout << sizeof( x ) << std::flush;
        return *this;
    }
};

template< typename U >
struct derived : public base< U > {
    using base<U>::operator<<;

    derived const & operator<<( float const & x ) const {
        std::cout << "derived" << std::flush;
        return *this;
    }
};

int main() {
    unsigned char c( 3 );
    derived< double > d;
    d << c;
    d.operator<<( c );
    return 0;
}

请您解释所涉及的规则,以获得上述代码的正确答案(与模板相关的重载和覆盖,整体提升......)?有效吗?如果规则太长,请提供文献指南。最新的编译器不同意正确的结果。 gcc-4.6和icpc-12.1.0声明“11”是正确答案,但VS2010由于含糊不清而拒绝编译d << c;但接受d.operator<<( c );。后者输出1 iirc。那么谁是对的,谁错了?

2 个答案:

答案 0 :(得分:3)

“11”是正确的输出。

对于两个表达式,派生运算符&lt;&lt;和基本运算符&lt;&lt;是候选人。然后基于它们所需的隐式转换序列来比较候选者。因为基本运算符&lt;&lt;是一个模板函数,其中推导出类型T以匹配它出现的参数,在两种情况下都是更好的匹配。

确切的规则很长。您可以在当前C ++草案标准的第13.3节重载决议中找到详细信息,n3337链接到工作组论文的this list

如果你问为什么MSVC不编译一个语句,我不完全确定,但是当有多个计算的ICS彼此不比时(如13.3中定义的那样),函数调用是不明确的。 3)。在d << c的情况下,MSVC似乎计算了至少一个重载的错误ICS,但诊断没有提供更多细节:

error C2666: 'derived<U>::operator <<' : 2 overloads have similar conversions
      with
      [
          U=double
      ]
      ConsoleApplication1.cpp(24): could be 'const derived<U> &derived<U>::operator <<(const float &) const'
      with
      [
          U=double
      ]
      ConsoleApplication1.cpp(14): or       'const base<U> &base<U>::operator <<<unsigned char>(T) const'
      with
      [
          U=double,
          T=unsigned char
      ]
      while trying to match the argument list '(derived<U>, unsigned char)'
      with
      [
          U=double
      ]

答案 1 :(得分:0)

它无法编译,因为您要求operator <<自动调用。这就像拥有operator +一样,并且拥有可以转换为基本类型的转换运算符(例如,转换为int)。例如:

class Conversion
{
public:
    operator int()
    {
        return 5;
    }

    int operator +(int x)
    {
        return 10;
    }
};

使用它像:

Conversion conv;
conv + 1.0;

此处operator +无法隐式调用,因为operator int也是可能的。但是,如果您通过int,则会调用operator +(int)。要使用double调用operator +,我们可以这样做:

conv.operator+(1.0);

我不了解编译器规则和严格的标准定义。

我还发现,如果我们将basederived类更改为非模板类​​,问题仍将存在(在VC10 / 11中):

struct base {   
    base const & operator<<( int x ) const { 
        std::cout << sizeof( x ) << std::flush; 
        return *this; 
    } 
}; 

struct derived : public base{ 
    using base::operator<<; 
    derived const & operator<<( float const & x ) const { 
        std::cout << "derived" << std::flush; 
        return *this; 
    } 
}; 

int main()
{
  derived d;
  d<<10.0;  // ERROR
}