Minh-Phuc Le
Minh-Phuc Le

Reputation: 90

How is function resolution done for C++ templates?

The code that baffles me is posted here:

namespace ns1 {
    struct myStruct1 {};
    struct myStruct2 {};
}

namespace ns2 {
    template <typename T>
    constexpr int foo(T& x) {
        return 1;
    }

    // If the two functions below are switched, it returns 2 correctly
    template <typename T>
    constexpr int fooCaller(T& x) {
        return foo(x);
    }

    constexpr int foo(ns1::myStruct2& x) {
        return 2;
    }
}

// If the below is uncommented, it also returns 2 correctly
/*
namespace ns1 {
    constexpr int foo(myStruct2& x) {
        return 2;
    }
}
*/

int main() {
    ns1::myStruct1 struct1;
    constexpr int struct1Foo1 = ns2::foo(struct1);
    static_assert(struct1Foo1 == 1);
    constexpr int struct1Foo2 = ns2::fooCaller(struct1);
    static_assert(struct1Foo2 == 1);

    ns1::myStruct2 struct2;
    constexpr int struct2Foo1 = ns2::foo(struct2);
    static_assert(struct2Foo1 == 2);
    constexpr int struct2Foo2 = ns2::fooCaller(struct2);
    static_assert(struct2Foo2 == 2); // Assertion fails, returns 1 instead!
}

I am trying to overload a templated function (foo). If I understand correctly, the template code would be generated only when the function is called. By that time, the overloaded version of the function should have been declared (as you can see in the code) and name lookup should have picked up on that overloaded version.

I'm sure that the overloaded version have been defined because static_assert(struct2Foo1 == 1) returns True, which indicates that foo(ns1::myStruct2&) has been defined.

Another baffling thing is placing the overloaded version in namespace ns1 causes the templated function to choose the overloaded version instead. I understand that this might have happened thanks to ADL, but I am not sure why ADL should work when directly overloading in the same namespace does not.

So why is it not picking up the overloaded version when I placed it below the template declaration in the same namespace?

Upvotes: 3

Views: 106

Answers (1)

T.C.
T.C.

Reputation: 137301

Ordinary unqualified lookup of dependent names only considers declarations found in the template definition context. Your second foo in ns2 is therefore not found.

Argument-dependent lookup will consider declarations found in either the definition or the instantiation context, but it only looks into namespaces and classes associated with the arguments, here, ns1. Thus, a foo declared later in ns1 will be found, but not one in ns2.

Upvotes: 5

Related Questions