1
#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 );. 後者は1iircを出力します。では、誰が正しくて誰が間違っているのでしょうか?

4

2 に答える 2

3

「11」が正しい出力です。

どちらの式でも、派生 operator<< と基本 operator<< の両方が候補です。次に、必要な暗黙の変換シーケンスに基づいて候補が比較されます。ベース operator<< は、型 T が引数に一致すると推定されるテンプレート関数であるため、どちらの場合でもより適切に一致します。

正確なルールはかなり長いです。詳細については、セクション 13.3現在の C++ ドラフト標準のオーバーロード解決(n3337) を参照してください。これは、ワーキング グループの論文のこのリストにリンクされています。

MSVC が 1 つのステートメントをコンパイルしない理由を尋ねている場合、正確にはわかりませんが、相互に優れていない複数の計算された ICS がある場合 (13.3.3 で定義されているように)、関数呼び出しはあいまいです。の場合、MSVC は少なくとも 1 つのオーバーロードに対して間違った ICS を計算しているようですd << cが、診断ではそれ以上の詳細は示されません。

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
      ]
于 2012-02-22T14:45:19.607 に答える
0

operator <<自動的に呼び出されるように要求しているため、コンパイルされません。operator +を持っているだけでなく、基本型に変換できる変換演算子を持っているようなものです(たとえば、 to int)。例えば:

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

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

そしてそれを次のように使用します:

Conversion conv;
conv + 1.0;

こちらも可能であるoperator +ため、暗黙的に呼び出すことはできませんoperator intintただし、合格すると、operator +(int)が呼び出されます。で operator+ を呼び出すにはdouble、次のようにします。

conv.operator+(1.0);

コンパイラのルールや厳密な標準定義については知りません。

また、 およびクラスを非テンプレート クラスとして変更するbaseと、問題が残ることもわかりました (VC10/11 の場合)。derived

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
}
于 2012-02-22T15:19:04.913 に答える