Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Potential g++ template bug?

Tags:

c++

templates

g++

I've encountered some code which I think should compile, but doesn't. So I'm hoping some of the local standards experts here at SO can help :-).

I basically have some code which resembles this:

#include <iostream>

template <class T = int>
class A {
public:
    class U {
    };

public:
    U f() const { return U(); }
};

// test either the work around or the code I want...
#ifndef USE_FIX
template <class T>
bool operator==(const typename A<T>::U &x, int y) {
    return true;
}
#else
typedef A<int> AI;
bool operator==(const AI::U &x, int y) {
    return true;
}
#endif

int main() {
    A<int> a;
    std::cout << (a.f() == 1) << std::endl;
}

So, to describe what is going on here. I have a class template (A) which has an internal class (U) and at least one member function which can return an instance of that internal class (f()).

Then I am attempting to create an operator== function which compares this internal type to some other type (in this case an int, but it doesn't seem to matter).

When USE_FIX is not defined I get the following error:

test.cc: In function 'int main()':
test.cc:27:25: error: no match for 'operator==' in 'a.A<T>::f [with T = int]() == 1'

Which seems odd, because I am clearly (I think) defining a templated operator== which should cover this, in fact if I just do a little of the work for the compiler (enable USE_FIX), then I no longer get an error. Unfortunately, the "fix" doesn't work generically, only for a specific instantiation of the template.

Is this supposed to work as I expected? Or is this simply not allowed?

BTW: if it matters I am using gcc 4.5.2.

like image 330
Evan Teran Avatar asked Jan 13 '11 07:01

Evan Teran


2 Answers

The problem with const typename A<T>::U &x is that U is a dependent type and the compiler cannot deduce T from the argument (this is one of the nondeduced context).

You could, for example, have two specializations of A:

class X { };
class Y { };
class Z { };

template <> class A<X> {
public: 
    typedef Z U;
};

template <> class A<Y> {
public:
    typedef Z U;
};

If you then call:

Z a;
a == 1;

what should the compiler deduce T as? X? Y?

One solution in this particular case is to declare operator== as a nontemplate friend inside of the class template:

template <class T = int>
class A {
public:
    class U {
    };

    friend bool operator==(const U& x, int y) {
        return true;
    }

public:
    U f() const { return U(); }
};
like image 131
James McNellis Avatar answered Oct 24 '22 05:10

James McNellis


template <class T>
bool operator==(const typename A<T>::U &x, int y) {
    return true;
}

Using this template, it is not permissible (or sometimes possible) to deduce the template parameter T from the type of x. It is what is known as a non-deducible context. (E.g. Somebody could specialize A for a different parameter, say double and make A<double>::U a typedef for A<int>::U.)

There is no workaround, you would have to explicitly specify the template parameter which for operator== makes for ugly syntax.

like image 28
CB Bailey Avatar answered Oct 24 '22 04:10

CB Bailey