Соответствует стандарту ISO C ++ следующий код - PullRequest
1 голос
/ 22 февраля 2012
#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 ]

3 голосов
/ 22 февраля 2012

«11» - правильный вывод.

Для обоих выражений оба производных оператора << и базовый оператор << являются кандидатами. Затем кандидаты сравниваются на основе неявных последовательностей преобразования, которые им необходимы. Поскольку базовый оператор << является шаблонной функцией, в которой тип T был выведен для соответствия аргументу, он подходит как лучшее соответствие в обоих случаях. </p>

Точные правила довольно длинные. Вы можете найти подробности в разделе 13.3 Разрешение перегрузки текущего проекта стандарта C ++, n3337, на которое есть ссылка в этом списке документов рабочей группы.

Если вы спрашиваете, почему MSVC не компилирует один оператор, я не совсем уверен, но вызов функции неоднозначен, когда есть несколько вычисляемых ICS, которые не лучше друг друга (как определено в 13.3. 3). MSVC, кажется, вычисляет неправильную 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
      ]
0 голосов
/ 22 февраля 2012

Он не компилируется, потому что вы просите, чтобы 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, мы можем сделать:

conv.operator+(1.0);

Я не знаю о правилах компилятора и строгом определении стандартов .

Я также обнаружил, что если мы изменим классы base и derived как не шаблонные классы, проблема все равно останется (в 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
}
...