简体   繁体   中英

template overload resolution trouble

Given this code:

#include <string>
#include <vector>
#include <iostream>

template <typename T>
std::string stringify(const T&) {
    return "{?}";
}

template <typename T>
std::string proxy(const T& in) {
    return stringify(in);
}

// trying to specialize "stringify()"

template <typename T>
std::string stringify(const std::vector<T>& in) {
    return "vector specialization!";
}

template <>
std::string stringify(const std::vector<int>& in) {
    return "INT vector specialization!";
}

int main() {
    std::cout << proxy(1); // calls the 1st

    std::vector<int> intVec;
    std::cout << proxy(intVec); // calls the 1st

    std::vector<double> dblVec;
    std::cout << proxy(dblVec); // calls the 1st

    return 0;
}

How can I specialize stringify() for vector<> after proxy<> ?

Currently I get {?}{?}{?}

If I delete this one - stringify(const std::vector<T>& in) then the vector<int> starts getting called because it would be a specialization of the first.

Then I would get {?}INT vector specialization!{?}

Is there any way to call any of the 2 vector specialization stringification functions from proxy() - if they are defined last - after the proxy() function?

Is there a way to partially specialize on vector<> and still get called from proxy<> ?

I don't want to specialize for vector<int> , vector<double> , vector<UserType> ...

EDIT: forgot to mention I need this for C++98

First of all, avoid specializing function templates, prefer overloading. See Herb Sutter's article on the potential pitfalls.

Secondly, the issue you're running into involves how name lookup works for dependent names in function templates. Inside proxy<T> , stringify is a dependent name - it depends on T . That name will be looked up at the point of definition of the template (which will find stringify<T>(const T&) and not the other overload) and again at the point of instantiation in the associated namespace of the arguments (which would be std ). Neither of those lookups find your other functions.

It's that second part of lookup - the argument-dependent lookup - that we can take advantage of. Let's just stick everything in one namespace (which I'm naming N arbitrarily, feel free to rename as appropriate):

namespace N {
    struct helper { };

    template <typename T>
    std::string stringify(helper, const T&) {
        return "{?}";
    }
}

template <typename T>
std::string proxy(const T& in) {
    return stringify(N::helper(), in);
}

Ok, so far we've changed absolutely nothing. We still get {?} in all cases. But the now we can stick further overloads (not specializations) of stringify still in that namespace but after the definition of proxy :

namespace N {    
    template <typename T>
    std::string stringify(helper, const std::vector<T>& ) {
        return "vector overload!";
    }

    std::string stringify(helper, const std::vector<int>& ) {
        return "INT vector overload!";
    }
}

Those two overloads will be found by the second phase of the name lookup because N is an associated namespace of helper . Now proxy(intVFec) will find all three overloads of stringify instead of just the one. And now your code prints:

{?}INT vector overload!vector overload!

as desired. None of the above requires C++11.

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