简体   繁体   中英

g++ compilation error of another thing that works in msvs

Heya, folks. I'm porting some code from a project largely developed in MSVS to use g++. I've found a lot of little differences, mostly things that MSVS allows but g++ does not. Usually it's something involving c++ standards, things that MSVS lets slide, but I'm having trouble seeing just what's wrong with one particular section.

g++ is having trouble matching a call to operator !=, but only in a specific context. Looking up operator != for a particular nested class works if the hosting class is not a template. If I turn the hosting class into a class template, however, everything breaks. I'm either missing something fundamental to c++, or g++ is doing something wrong.

I've learned not to cry "Compiler Bug!" too often, so I wanted to see if anyone here can see what I'm missing.

This worked example shows the working, non-template version, and then the broken, template version. g++ --version gives: g++ (Ubuntu 4.4.1-4ubuntu9) 4.4.1

Working reference version without templates

namespace Works {

struct host {
    struct iterator {};
    iterator op();
};

bool operator != (host::iterator const& a0, host::iterator const& a1);

bool f() {
    return host().op() != host().op();
}

} // namespace Works

Broken version with templates

namespace Broken {

template <typename T>
struct host {
    struct iterator {};
    iterator op();
};

template <typename T>
bool operator != (typename host<T>::iterator const& a0, 
                   typename host<T>::iterator const& a1);

bool f() {
    return host<int>().op() != host<int>().op();
}

} // namespace Broken

The template version fails with the errors:

Main.cpp: In function ‘bool Broken::f()’:
Main.cpp:50: error: no match for ‘operator!=’ in ‘Broken::host<int>().Broken::host<T>::op [with T = int]() != Broken::host<int>().Broken::host<T>::op [with T = int]()’

This works neither in msvc nor gcc.

The problem is that in host<T>::iterator , T is in non-deducible context. Since neither parameter allows T to be deduced, the function template cannot be instantiated.

That's why you usually define overloaded operators inside the class.

struct iterator
{
    friend bool operator != (iterator const & lhs, iterator const & rhs)
    {
        return false;
    }
};

i hit a similar thing. C++ operator overloads are supposed to take const inputs. msvc will let you get away with using non const inputs. G++ insists they are const

Building an @avakar's answer, I got the code to work properly, though it's a little strange:

namespace Broken {

template <typename T> struct host;

template <typename T>
bool operator != (typename host<T>::iterator const& a0,
                  typename host<T>::iterator const& a1);

template <typename T>
struct host {
    struct iterator {
       friend bool operator !=(iterator const & lhs, iterator const & rhs) {
          return operator !=<int>(lhs, rhs);
       }
    };
    iterator op();
};

bool f() {
    // The following does indeed end up calling the operator != you want it to call.  I
    // have a slightly different version in which I tested this.
    return host<int>().op() != host<int>().op();
}

} // namespace Broken

This gets around the problem that the template arguments to the top level operator != cannot be deduced by calling it with an explicit template argument from a friend function. It requires forward declaring the host template.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM