Victor Drouin
Victor Drouin

Reputation: 627

Template specialization within namespace

I was messing around with some code when this weird behavior occurred :

In the first test, base templated function, user and templated specialization lie within the same namespace and it behaves as I expect :

    namespace Test1
    {
        template <typename V, typename T>
        int doFoo(V& a_visitor, T& a_value)
        {
            return 0;
        }

        struct Foo
        {
            template <typename T>
            int process(T const& a_value)
            {
                return doFoo(*this, a_value);
            }
        };

        template <typename T>
        int doFoo(Foo& a_vis, T const& a_ptr)
        {
            return 1;
        }
    }

    int main()
    {
        int const k{ 42 };

        return Test1::Foo{}.process(k); // returns 1
    }

but when I move base templated function and its specialization in another namespace, the base one is selected :

namespace Test2
{
    namespace b
    {
        template <typename V, typename T>
        int doBar(V& a_visitor, T& a_value)
        {
            return 0;
        }
    }

    struct Bar
    {
        template <typename T>
        int process(T const& a_value)
        {
            return b::doBar(*this, a_value);
        }
    };

    namespace b
    {
        template <typename T>
        int doBar(Bar& a_vis, T const& a_ptr)
        {
            return 1;
        }
    }
}

int main()
{
    int const k{ 17 };

    return Test2::Bar{}.process(k); // returns 0
}

EDIT I can do even weirder : in example 1 if I replace call to doFoo with Test1::doFoo I get the wrong behavior again !

Could anyone explain me what is going on here ? How can I do if I really need struct Bar not to be within namespace b ?

Upvotes: 3

Views: 150

Answers (1)

To begin with, those aren't specializations, those are overloads. Completely different function templates that aren't related to each other.

The behavior you see is consistent with argument-dependent lookup. When encountering an unqualified function call, the compiler builds an overload set by examining the namespaces associated with each argument to the function call.

Normally this won't find declarations the come "after", but templates are special. In templates lookup for a dependent name, such as a function call that depends on a template parameter (the type of a_value), is performed after the template is instantiated, and not at the point of definition. That occurs in main, after the namespace is complete and all overloads are available, and so ADL finds the second overload.

This is also why when you qualify the call by Test1 you no longer find the second overload. That negates ADL, and only allows for overloads that appear prior to the point of the call. The easiest way to resolve it would probably be to delay the definition of process until all overloads are available, as other answers indicate.

Upvotes: 2

Related Questions