I'm trying to compile with g++ some code previously developed under Visual C++ 2008 Express Edition, and it looks like g++ won't let me call a template method on a reference returned by a method of a template variable. I was able to narrow the problem down to the following code:
class Inner
{
public:
template<typename T>
T get() const
{
return static_cast<T>(value_);
};
private:
int value_;
};
class Outer
{
public:
Inner const& get_inner() { return inner_; };
private:
Inner inner_;
};
template<typename T>
int do_outer(T& val)
{
return val.get_inner().get<int>();
}
int main()
{
Outer outer;
do_outer(outer);
return 0;
}
The code compiles fine under Microsoft's compiler, but g++ throws an error:
$ g++ -c main.cpp
main.cpp: In function ‘int do_outer(T&)’:
main.cpp:24: error: expected primary-expression before ‘int’
main.cpp:24: error: expected ‘;’ before ‘int’
main.cpp:24: error: expected unqualified-id before ‘>’ token
where line 24 refers to return val.get_inner().get<int>();
.
If I make do_outer
a normal method receiving an Outer
reference the code compiles. Making Inner::get()
a normal method also works. And making Inner::get()
return void and receive a template parameter also works because the int specifier below becomes needless, i.e.:
class Inner
{
public:
template<typename T>
void get(T& val) const
{
val = static_cast<T>(value_);
};
private:
int value_;
};
...
template<typename T>
int do_outer(T& val)
{
int i;
val.get_inner().get(i);
return i;
}
...
(g++ doesn't complaing about the code above.)
Now I'm out of ideas. What's the problem? Is there a problem with gcc/g++? Is there a compliance issue with my code?
The compiler I'm using is:
$ g++ --version
g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3
No, template member functions cannot be virtual.
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
Just to give some background on why the template
keyword is needed:
template<typename T>
int do_outer(T& val)
{
int i;
val.get_inner().get<int>(i);
return i;
}
When the compiler sees this function, it does not know what the type of val
is. It therefore parses the line val.get_inner().get(i)
as follows:
1: val .
The compiler sees the .
and so can assume that 'val' has class type and the next identifier is the name of a member object or function.
2. val . get_inner (
get_inner
is the name of the member and then the compiler sees the (
. The only possibility is that get_inner
is a function name and so this is a function call. It then parses the parameters until it finds the closing )
.
3. val . get_inner () .
As for the first step, it now knows that the return from get_inner must be a class type so it knows that the next identifier is a member object or function.
4. val . get_inner () . get <
So, what can the <
possibly mean? Of course it's the start of template arguments...or maybe it's the less than operator?
We know that get
can only be an object or a function. If it is an object then the <
makes perfect sense as the less than operator. Furthermore, the standard more or less states that only where the name before the <
is a template-name
will it treat the <
as template arguments (14.2/3):
After name lookup (3.4) finds that a name is a template-name, if this name is followed by a
<
, the<
is always taken as the beginning of a template-argument-list and never as a name followed by the less-than operator.
In this case, the compiler has no idea what the type of the expression val.get_inner()
is and so it cannot lookup get
. It more or less assumes then that it's a member object and not a template-name. '<' is treated as the less than operator and the compiler ends up checking if get
is less than int
- hence the error.
So, why do the fixes work?
Adding the template
keyword
Literally we're telling the compiler that the get
is a template-name and so the <
operator is treated as the start of a template argument list.
Removing the template-arguments
When do_outer doesn't have the template arguments ie: val . get_inner () . get (
the compiler expects that the member get
is an object or a function. The (
disambiguates between these two and the name is treated as a function. Later template argument deduction then works out the type of the template parameter.
could you try with?
template<typename T>
int do_outer(T& val)
{
return val.get_inner().template get<int>();
}
I don't have access to gcc atm, but I've had similar issues and adding the template keyword always solved them. And it works in VS too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With