Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

g++ doesn't like template method chaining on template var?

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
like image 953
Rômulo Ceccon Avatar asked Oct 15 '09 20:10

Rômulo Ceccon


People also ask

Can a virtual function be templated?

No, template member functions cannot be virtual.

How do I restrict a template type in C++?

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.

Can a non templated class have a templated member function?

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.

Can a template be a template parameter?

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.)


2 Answers

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.

like image 137
Richard Corden Avatar answered Oct 05 '22 06:10

Richard Corden


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.

like image 20
stijn Avatar answered Oct 05 '22 05:10

stijn