8

I have this source code which allows me to cast a Point<float> to Point<double>:

template<class T> struct Point{
    template <typename NewType> Point<NewType> cast() const{
        return Point<NewType>();
    }       
};

int main(){
    Point<float> p1;
    Point<double> p2;
    p2 = p1.cast<double>();

    return 0;
}

This source code compiles well. Now, I add the following class and I have a compilation error at the line that does the cast:

template <class T> struct PointContainer{
    void test(){
        Point<T> p1;
        Point<double> p2;
        p2 = p1.cast<double>(); //compilation error
    }
};

Compilation error: error: expected primary-expression before ‘double’.

Why do I get this error and how can I solve it?

melpomene
  • 84,125
  • 8
  • 85
  • 148
Saelhenen
  • 163
  • 1
  • 9
  • 3
    Related: http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords?rq=1 ("*The "template" keyword*") – melpomene Jun 23 '16 at 06:15

5 Answers5

9

Short answer: you need to add the keyword template right after the member access operator (the dot) in the line that is giving you the error.

Long answer: in PointContainer<T>, T is an unknown template variable. Since in C++ templates can have specializations that are allowed to be completely different from the base template, the compiler does not know if type Point<T> has a member cast that is a function template before substituting T for an actual type. That is why you can use the cast in main: Point<float> is a concrete type and the compiler knows that it does have that function template.

Since the compiler does not know the type of member that "cast" will be, it does not let you use operations that assume that it is a type, a template, or both. In order to do either you need to tell that to the compiler using the typename and template keywords. Imagine that in addition to your cast function template there is a typedef T value_type in Point<T>:

Point<float>::value_type f = 1.0f; // OK
Point<T>::value_type f = 1.0f; // Bad, compiler does not identify that as a type
typename Point<T>::value_type f = 1.0f; // OK

Point<float> pf;
Point<T> pT;
Point<double> pd1 = pf.cast<double>(); // OK
Point<double> pd2 = pT.cast<double>(); // Bad, compiler does not know cast is a template
Point<double> pd3 = pT.template cast<double>(); // OK
Javier Martín
  • 2,537
  • 10
  • 15
5

Point<T> is a dependent-name (it depends on the template parameter T), so the compiler does not really know what Point<T>::cast is:

  • Point<T>::cast could be an attribute, then p1.cast < would be seen as a comparison with what's next.
  • Point<T>::cast could be a template (your case).

Since the compiler cannot know, it assumes the first one, hence your error (you cannot compare something with a type). To solve this, you need to explicitly tell the compiler that p1.cast is a templated method:

p2 = p1.template cast<double>();

For more information, see this question: Where and why do I have to put the "template" and "typename" keywords?

Community
  • 1
  • 1
Holt
  • 36,600
  • 7
  • 92
  • 139
1
    p2 = p1.cast<double>();

needs to be

    p2 = p1.template cast<double>(); 

The reason for that can be found at Where and why do I have to put the "template" and "typename" keywords?.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

The accepted answer in the post melpomene linked gives you the answer.

You need to use the template keyword.

template <class T> struct PointContainer{
    void test(){
        Point<T> p1;
        Point<double> p2;
        p2 = p1.template cast<double>(); 
    }
};
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

As everyone else has said, your problem is that you need to tell the compiler that dependent templates are templates with an annoying use of the template keyword.

 y=x.template cast<Y>();

The way around this is to make it no longer a dependent template.

The easiest method is to write a free function

template<class D, class S>
Point<D> cast( Point<S> const& s ){ return s.template cast<D>(); }

Now you can freely use:

y = cast<Y>(x);

without the annoying template keyword, because the fact that cast is a template is no longer dependent.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524