проблема разрешения перегрузки шаблона


Учитывая этот код:

#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;
}

Как я могу специализироваться stringify() для vector<> после proxy<>?

В настоящее время я получаю {?}{?}{?}

Если я удалю этот - stringify(const std::vector<T>& in), то vector<int> начнет вызываться, потому что это будет специализация первого.

Тогда я получу {?}INT vector specialization!{?}

Есть ли способ вызвать любую из 2 векторных функций специализации stringification из proxy() - если они определены последними-после функции proxy()?

Есть ли способ частично специализируетесь на vector<> и все равно получаете вызов от proxy<>?

Я не хочу специализироваться на vector<int>, vector<double>, vector<UserType>...

EDIT: забыл упомянуть, что мне это нужно для C++98

1 2

1 ответ:

Прежде всего, избегайте специализированных шаблонов функций, предпочитайте перегрузку. См. Статья Херб Саттер на потенциальные подводные камни.

Во-вторых, проблема, с которой вы столкнулись, связана с тем, как поиск имен работает для зависимых имен в шаблонах функций. Внутри proxy<T>, stringify есть зависимое имя-оно зависит от T. Это имя будет искать в точке определения шаблона (которая найдет stringify<T>(const T&), а не другую перегрузку) и снова в точке создания экземпляра в связанном пространстве имен аргументов (которое будет std). Ни один из этих поисков не находит других ваших функций.

Это та вторая часть поиска - Поиск, зависящий от аргумента,-которой мы можем воспользоваться. Давайте просто поместим все в одно пространство имен (которое я называю N произвольно, не стесняйтесь переименовывать по мере необходимости):

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);
}
Хорошо, до сих пор мы абсолютно ничего не изменили. Мы все равно получаем {?} во всех случаях. Но теперь мы можем держаться дальше перегружает (не специализации) stringify все еще в этом пространстве имен, но после определения 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!";
    }
}
Эти две перегрузки будут найдены на втором этапе поиска имени, поскольку N является ассоциированным пространством имен helper. Теперь proxy(intVFec) найдет все три перегрузки stringify вместо одной. И теперь ваш код печатает:
{?}INT vector overload!vector overload!

Как пожелано. Ни для чего из вышеперечисленного не требуется C++11.