views:

220

answers:

4

This code compiles in CodeGear 2009 and Visual Studio 2010 but not gcc. Why?

class Foo
{
public:
    operator int() const;

    template <typename T> T get() const { return this->operator T(); }
};

Foo::operator int() const
{
    return 5;
}

The error message is:

test.cpp: In member function `T Foo::get() const':
test.cpp:6: error: 'const class Foo' has no member named 'operator T'

+4  A: 

I'm not sure what the exact rules for the names of C++ operators are, but I believe it's trying to call operator T() instead of operator int(). Why not just use a cast:

template <typename T> T get() const { return static_cast<T>(*this); }

I haven't tested this but I believe this will accomplish more or less the same thing. If not, there should be a way to accomplish this without having to call operator T() directly. That's what overloaded operators are for, after all.

Chris Lutz
No need for that cast. :)
GMan
@GMan - Wasn't sure, leaving it in because UncleBens already has it without.
Chris Lutz
Does the static_cast explicitly call the overloaded casting operator? I want to avoid accidentally calling int instead of long or double instead of float, etc.
Jonathan Swinney
Looks like it doesn't. I tested it.
Jonathan Swinney
+3  A: 

If I modify the code slightly to this:

template <typename T> T get() const { return operator T(); }

I get the following with GCC:

there are no arguments to 'operator T' that depend on a template parameter, so a declaration of 'operator T' must be available (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)

But if you were to use -fpermissive, the whole thing would only work if T = int

On the other hand, why not do:

template <typename T> T get() const { return *this; }

That's sort of the point of the conversion operator.

UncleBens
Because I want to explicitly call the operator for a particular type without allowing the compiler to call float when I meant double or int when I meant long.
Jonathan Swinney
@Jonathan: I can't really see a reason to make extensive use of conversion functions, particularly if you want to keep good track of what gets converted to what.
UncleBens
+6  A: 

It's a bug in G++. operator T is an unqualified dependent name (because it has T in it and lookup will thus be different depending on its type). As such it has to be looked up when instantiating. The Standard rules

Two names are the same if

  • ...
  • they are the names of user-defined conversion functions formed with the same type.

Thus the type name specified after the operator keyword doesn't have to match lexically in any way. You can apply the following work-around to force GCC treating it as a dependent name

template<typename T, typename>
struct identity { typedef T type; };

class Foo
{
public:
    operator int() const;

    template <typename T> T get() const { 
      return this->identity<Foo, T>::type::operator T(); 
    }
};
Johannes Schaub - litb
That does work, but it seems like an awkward work around. Thanks for your help!
Jonathan Swinney
@Jonathan, yes and it breaks with virtual `operator int`. You can do `static_cast<typename identity<Foo, T>::type*>(this)->operator T();` if you want to have a virtual call.
Johannes Schaub - litb
+2  A: 

I'm s bit curious about the use case for this. I suppose it is so you can say:

Foo f;
int x = f.get<int>();

but why not say:

Foo f;
int x = static_cast<int>(f);

which is slightly longer to type (though one could use the C cast notation), but doesn't need any hoop jumping.

anon