문제

#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;
}

Can you please explain the rules involved to obtain a correct answer of the above code (overloading and overriding in connection with templates, integral promotion, ...)? Is it valid? If the rules are too lengthy please provide literature pointers. The latest compilers disagree about the correct result. gcc-4.6 and icpc-12.1.0 claim "11" is the correct answer but VS2010 refuses to compile d << c; due to ambiguities but accepts d.operator<<( c );. The latter outputs 1 iirc. So who's right and who's wrong?

도움이 되었습니까?

해결책

"11" is the correct output.

For both expressions both the derived operator<< and base operator<< are candidates. Then the candidates are compared based on the implicit conversion sequences they require. Because the base operator<< is a template function where the type T has been deduced to match the argument it comes up as the better match in both cases.

The exact rules are pretty long. You can find the details in section 13.3 Overload resolution of the current C++ draft standard, n3337 linked to in this list of the working group's papers.

If you're asking why MSVC doesn't compile the one statement, I'm not exactly sure, but a function call is ambiguous when there are multiple computed ICSs that are not better than each other (as defined in 13.3.3). MSVC seems to be computing the wrong ICS for at least one of the overloads in the case of d << c, but the diagnostic doesn't give any more details:

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
      ]

다른 팁

It doesn't compile because you are asking operator << to be invoked automatically. It is like having operator +, as well as having conversion operator that can convert to basic type (say, to int). For example:

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

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

And using it like:

Conversion conv;
conv + 1.0;

Here operator + cannot be implicitly invoked, since operator int is also possible. If you pass, int, however, operator +(int) will be called. To invoke operator+ with double, we can do:

conv.operator+(1.0);

I don't know about the compiler rules, and the strict standards definition.

I also found out, that if we change the base and derived classes as non-template classes, the problem would still remain (in 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
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top