简体   繁体   中英

C++ adding friend to a template class in order to typecast

I'm currently reading "Effective C++" and there is a chapter that contains code similiar to this:

template <typename T>
class Num {
public:
  Num(int n) { ... }
};

template <typename T>
Num<T> operator*(const Num<T>& lhs, const Num<T>& rhs) { ... }

Num<int> n = 5 * Num<int>(10);

The book says that this won't work (and indeed it doesn't) because you can't expect the compiler to use implicit typecasting to specialize a template.

As a soluting it is suggested to use the "friend" syntax to define the function inside the class.

//It works
template <typename T>
class Num {
public:
  Num(int n) { ... }

  friend 
  Num operator*(const Num& lhs, const Num& rhs) { ... }
};

Num<int> n = 5 * Num<int>(10);

And the book suggests to use this friend-declaration thing whenever I need implicit conversion to a template class type. And it all seems to make sense.

But why can't I get the same example working with a common function, not an operator?

template <typename T>
class Num {
public:
  Num(int n) { ... }

  friend 
  void doFoo(const Num& lhs) { ... }
};

doFoo(5);

This time the compiler complaints that he can't find any 'doFoo' at all. And if i declare the doFoo outside the class, i get the reasonable mismatched types error. Seems like the "friend ..." part is just being ignored.

So is there a problem with my understanding? What is the difference between a function and an operator in this case?

The reason is that here

doFoo(5);

the compiler has no way of finding foo , given an int parameter. This would be the equivalent of calling your friend operator like this:

Num<int> n = 5 * 10;

This will "work", but not by calling the friend operator* defined in your Num class, but by calling the built-in operator* for integers, and then using the implicit conversion from Num 's converting constructor.

The core problem is lookup. A friend declaration provides a declaration of a namespace level function, but the declaration is only available inside the class that is befriending it. In the example the book provides that is not an issue: the function takes two arguments of the enclosing type, as long as one of them is of the enclosing type, Argument Dependent Lookup will look inside the definition of the class and find the operator. In your case that is not the case, since there is a single argument and that needs a conversion, the compiler will not look inside the definition of the class.

Note that this is regardless of templates and conversions:

class A {
   friend void f( int ) {}
   friend void g( int, A ) {}
};
int main() {
   f(5);           // Error: lookup cannot find 'f' declared *only* inside A
   g(5,A());       // Ok, one argument is 'A', lookup will find the function
}

In the case above, where there are no templates involved, you could potentially add a declaration at namespace level to fix it, but that is not really an option for template classes.

class A {
   friend void f() { std::cout << "inside A\n"; }
};
void f(int);     // only declaration
int main() {
   f(5);         // "inside A"
}

This cannot be done for a template (and for all instantiating types) as the friend declaration is a declaration of a non-templated function. Although you could can play with the code just for the sake of testing:

template <typename T>
struct Num {
   Num(int x) ...
   friend void f( Num const & );
};
Num<int> f(Num<int> const &);    // only declaration
int main() {
   f(5);
}

Yes these code compiler do not know how to work with it . like doFoo(5)

compiler do not know 5 is int

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM