3

I wrote a short program to test the template class's explicit instantiation as follows:

#include <iostream>

template <class T>
struct less_than_comparable {
    friend bool operator>=(T const& a, T const& b) {
        return !(a < b);
    }
};

class Point {
    friend bool operator<(Point const& a, Point const& b) {
        return a.x_ < b.x_;
    }

public:
    Point(int x) : x_(x) {}

private:
    int x_;
};

template struct less_than_comparable<Point>; 
//Here I explicitly instantiate a template class expecting that 
//less_han_comparable<Point> would export opeartor>=() for class Point to the global
//namespace, and then the p1 >= p2 statement will work as expected.

int main() {
    using namespace std;

    Point p1(1), p2(2);
    cout << (p1 < p2) << endl;
    cout << (p1 >= p2) << endl; //But I have a compiler error here saying that 
                                //no match for ‘operator>=’ in ‘p1 >= p2’
}

I know if I inherit Point from less_than_comparable, the code will pass the compiling. But my question is why it doesn't work if I use explicit instantiation? I use G++ 4.4.5 running on Ubuntu 10.04. Any comments will be appreciated. Thanks.

Brooks Xi
  • 33
  • 3
  • Simple answer, it doesn't work because the language doesn't work like that. Why should the compiler select the struct's friend function in this case? –  Apr 25 '11 at 08:19

4 Answers4

2

The problem is that friend functions defined inside class-like types are not injected into enclosing namespace.

The principle you are refering to is called "friend name injection", but this has been replaced in current C++ standard by "ADL" (Argument Dependent Lookup, also called Koenig Lookup). ADL examines all namespaces associated with function parameter types for matching function.

In your case, when you call operator>= in p1 >= p2 (i.e. operator>=(p1, p2);). ADL looks for matching function in the namespace of Point, but Point doesn't have such function.

If you inherit Point from less_than_comparable, operator>= becomes part of namespace of Point and ADL can find it here.

You can check, that no friend name injection takes place here.

Vitus
  • 11,822
  • 7
  • 37
  • 64
  • Finally! the answer I´ve been looking for :) The link on no friend name injection looks to be dead, though :-/ – Drodbar Jul 30 '15 at 19:56
1

The code doesn't work because Point is not a template class where you have defined operator >=. If you want to compile this code then define operator >= in Point class as well. Note that p1 and p2 are no where related to less_than_comparable.

As side note, why you have defined operator for "greater than equal to" operator in the name of less_than_comparable ?

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • In fact, I'm studying the boost library. It has an operators library that defines the less_than_comparable template class. I just simulated a simplified version to demonstrate the concept, so here less_than_comparable comes. – Brooks Xi Apr 25 '11 at 09:03
  • @Brooks Xi : boost.operators uses [CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) like @Tony does in his answer; *that's* the proper approach. – ildjarn Apr 25 '11 at 09:08
1

Explicit instantiation is simply a way to force the compiler to compile a particular template within that translation unit - it has no affect on the lookup of names.

To get what you appear to want, you could for example:

class Point : public less_than_comparable<Point> {
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • I think what you said is the point: explicit instantiation has no affect on the lookup of names. Can you explain in more details how does the compiler look up for names of friend functions? Thanks a lot. – Brooks Xi Apr 25 '11 at 08:57
  • @Brooks Xi: a `friend` function is considered alongside other functions in the surrounding namespace to which it's added... it has no special privileges or behaviours other than access to the class's members, though it's noteworthy that a friend function defined inside the class is - like any other function defined in the class - implicitly inline, whereas often you'd need to make the function explicitly inline if you moved it to the surrounding namespace to avoid multiple definitions if including the header in multiple translation units (search "one definition rule"). – Tony Delroy Apr 25 '11 at 09:33
  • @Brooks: BTW / if you consider the CRTP solution I've provided (CRTP is another one to google if you're not already familiar with it), it derives from `less_than_comparable` which means the member contributed to Point is `operator>=(const Point&, const Point&)` and NOT still a template. – Tony Delroy Apr 25 '11 at 09:36
0

your operator >= is a member function of a completely different type that is unrelated to Point as far as the compiler is concerned. I think what you want to do is:

template< T >
bool operator >= ( T const& a, T const& b ) {... }

forget the class and just make it a global function. And you won't need explicit template instantiation. In fact the only time I've seen it used was when you had template class declared inside a library and used in another project, which you are obviously not doing here.

DXM
  • 4,413
  • 1
  • 19
  • 29