10

Why does this fail to compile with GCC 4.4?

template<typename T>
class A {
public:
    void foo () {

    }

private:
    T x;
};

namespace Ns {
template<typename T>
void do_it (A<T> a) {
    a.foo ();
}
};

template<typename T>
void myfun (T x) {
    Ns::do_it (x);
}

template<typename T>
class B {
public:
    void bar () {

    }

private:
    T x;
};

namespace Ns {
template<typename T>
void do_it (B<T> b) {
    b.bar ();
}
};

int main () {
    A<int> a;
    B<int> b;

    myfun (a);
    myfun (b); // error: no matching function call to do_it(B<int>&)

    return 0;
}

It must have something to do with the namespace of do_it. When I remove the namespace around it, it compiles.

Background: I am building a set of functions that may be used with many different container classes. To handle the different interfaces uniformly I use freestanding functions that are overloaded for each of the container classes. These functions shall be put into a namespace to avoid cluttering the global namespace with them.

The definitions for B shall be thought of as coming from a different header file than those for A so reordering is not an option.

2
  • VS 2010 compiles the above code, and I believe it is correct in doing so, but this is a tricky example. Nice question! Commented Apr 18, 2011 at 18:49
  • The same with VS2008, I just checked it. Could this be a bug in GCC or is their interpretation just different from Microsoft's? The fact that it works without a namespace would point to it being a bug, wouldn't it? Commented Apr 18, 2011 at 19:37

1 Answer 1

7

The reason is that only ADL is done at the point of the call. Other function lookups are only done in the definition of the myfun function template.

And at that definition context, only the do_it overload accepting the A<int> is declared.

Edit: If you want to have a Standard reference for this, refer to [temp.dep.candidate] and [temp.res]p1.

Sign up to request clarification or add additional context in comments.

6 Comments

@Mark if you want to have functions in namespace Ns considered, you can derive B<T> from a dummy class defined in that namespace, so that ADL will look into the Ns namespace. Or you can pass template arguments to B<T> that associate the generated specialization with Ns. For example B<NS::ADLAssociator<int>> (ADLAssociator could just be a small class template only storing T t;). Or you pass those associator classes as additional dummy arguments: B<int, NS::ADL>. There are many options for this one. The cleanest seems to be to just derive B from a class or define it in NS.
What about changing the free function into an static class member: namespace Ns { template <typename T> struct dispatch; } (declare it up front), then for each new type, add an specialization: namespace Ns { template <typename T> struct dispatch < B<T> > { static void do_it( B<T> x ) { x.bar() } }; }, and have template <typename T> void myfunc( T t ) { Ns::dispatch<T>::do_it( t ); }
@David yes that will work too, good idea. Won't depend on fragile ADL.
@JohannesSchaub-litb: Could you please elaborate this answer? Especially the two phrases "instantiation context" and "definition context"? Are they standard terms used in the spec? :-/
Why is ADL relevant in the OP's example? Ns::do_it should not perform ADL as the namespace is explicitly specified?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.