tags:

views:

125

answers:

1

I have a template class that defines a subtype. I'm trying to define the binary operator+ as a template function, but the compiler cannot resolve the template version of the operator+.

#include <iostream>
template<typename other_type> 
struct c {
  c(other_type v) : cs(v) {}
  struct subtype { subtype(other_type v) : val(v) {} other_type val; } cs;
};

template<typename other_type>
typename c<other_type>::subtype operator+(const typename c<other_type>::subtype& left,
                      const typename c<other_type>::subtype& right)
{ return typename c<other_type>::subtype(left.val + right.val); }

// This one works
// c<int>::subtype operator+(const c<int>::subtype& left,
//          const c<int>::subtype& right)
// { return c<int>::subtype(left.val + right.val); }

int main()
{
  c<int> c1 = 1;
  c<int> c2 = 2;
  c<int>::subtype cs3 = c1.cs + c2.cs;
  std::cerr << cs3.val << std::endl;
}

I think the reason is because the compiler (g++4.3) cannot guess the template type so it's searching for operator+<int> instead of operator+.

What's the reason for that? What elegant solution can you suggest?

+4  A: 

Your suspicion is correct. The compiler doesn't know what other_type is. It can't deduce it from the arguments. Such forms often are too loose to provide the needed informations. Consider

template<typename other_type> 
struct c {
  c(other_type v) : cs(v) {}
  typedef int subtype;
};

If you were to pass an int, then any of c<T> would fit the bill because all of them have type int. In the specific case of nested classes, it may be possible, but even then it's not unique. Imagine that in c<float> you could put a typedef c<int> subtype;, then both c<float>::subtype and c<int>::subtype would fit the bill.

Back in pre-standard time, there existed a list of template-issues that in particular John Spicer did go through and invented reasonable solutions. That was one such problem, and it was found that it's not worth the trouble. You always explicitly need to provide the argument - it's never deduced.

You could change your code to this

template<typename other_type> 
struct subtype { 
  subtype(other_type v) : val(v) {}
  other_type val; 
};

template<typename other_type> 
struct c {
  c(other_type v) : cs(v) {}
  subtype<other_type> cs;
};

template<typename other_type>
subtype<other_type> operator+(const subtype<other_type>& left,
                      const subtype<other_type>& right)
{ return subtype<other_type>(left.val + right.val); }

Or to this

template<typename other_type> 
struct c {
  c(other_type v) : cs(v) {}
  struct subtype { 
   subtype(other_type v) : val(v) {} other_type val; 

    /* note: don't forget friend! */
    friend subtype operator+(const subtype& left,
                      const subtype& right)
    { return subtype(left.val + right.val); }
  } cs;
};
Johannes Schaub - litb
+1 beaten to it; this is a better than mine was going to be anyway.
Charles Bailey
@Charles, thanks. appreciated
Johannes Schaub - litb
The second one reports an error (shadowing other_type) and if I change the name of the template type it gives the same error as before (and it seems that the type of this friend operator+ should be the same as the original one).
baol
@baol, the second template parameter clause isn't really needed. i removed it when i noticed they are called the same. The version as it is as of now should work fine.
Johannes Schaub - litb
Thank you. A really helpful answer. The only thing that I still don't get is why the "friend" version is different from the original one.
baol
@baol, the friend version is using a non-template declaration. It's a normal function declaration that, when the outer template is instantiated, gets placed into the surrounding namespace. So when that placing happens, the parameter types are fully known.
Johannes Schaub - litb