Специализированный шаблон для вектора произвольного типа

У меня есть шаблонная функция для преобразования объекта в строку (это из библиотеки, я не могу это изменить):

template <typename Q> std::wstring ToString(const Q& q) { static_assert(false, "Must specialise"); }

Теперь я хочу вызвать его с параметром std::vector<Gene>, где Gene — это простой класс, детали которого не важны. Конечно, для этого мне нужно специализировать шаблон, поэтому я делаю:

template<> std::wstring ToString(const std::vector<Gene>& q){...}

Предположим, у меня есть другой класс, Cell, и я хочу специализировать функцию ToString для класса std::vector<Cell>. Мне пришлось бы сделать еще одну явную специализацию с тем же телом, что и у версии std::vector<Gene>.

Логика преобразования std::vector не зависит от фактического типа контента (int, Gene, Cell, еще один std::vector и т. д.), поэтому имеет смысл создать специализацию шаблона, которая может работать с любым std::vector. Но я не могу найти простой способ сделать это. На данный момент у меня есть функция VectorToString, и я перенаправляю вызовы от ToString(std::vector<Gene>>) и ToString(std::vector<Cell>), но это все еще требует от меня реализации специализации для каждого типа элемента.

Итак, к собственно вопросу: можно ли создать единую специализацию для произвольного std::vector и как мне это сделать? И, в качестве бонуса, можно ли обобщить это на любую произвольную коллекцию, которая поддерживает std::begin и std::end?

Я просмотрел этот этот вопрос и попытался объявить специализацию следующим образом:

template<typename E> std::wstring ToString<std::vector<E>>(const std::vector<E>& t){...}

но это не удается с C2768: незаконное использование явных аргументов шаблона (компилятор не смог определить, должно ли определение функции быть явной специализацией шаблона функции или должно ли определение функции быть для новой функции.), что имеет смысл, поскольку у нас одинаковое имя функции и количество параметров шаблона, а также аналогичная сигнатура.

Для справки: я использую Visual C++ 2015 RC, а исходная функция шаблона происходит из родной тестовой библиотеки.


person Meindratheal    schedule 05.07.2015    source источник
comment
К сожалению, вы не можете частично специализировать функции, вы их перегружаете.   -  person grisha    schedule 06.07.2015


Ответы (3)


Вы почти сделали это правильно, но в случае функций вы не должны их специализировать, вы должны просто перегрузить их так:

template <typename Q> std::wstring ToString(const Q& q) {
    return L"Original";
}
template<typename E> std::wstring ToString(const std::vector<E>& t) {
    return L"overload";
}

См. http://ideone.com/G3r0Vt для рабочего примера.

Это работает, потому что при выборе перегрузки для вызова const std::vector<E> считается "лучше", чем const Q&, поэтому используется перегрузка.

При использовании этих методов в своем коде вы должны учитывать ADL< /а>.

person WorldSEnder    schedule 05.07.2015
comment
Я пробовал что-то подобное раньше, но, поскольку я думал, что специализируюсь, а не перегружаю, и функция на самом деле находится в пространстве имен, я написал ее как template<typename E> std::wstring Microsoft::VisualStudio::CppUnitTestFramework::ToString(const std::vector<E>& t), которая не работала, мне пришлось обернуть ее надлежащим образом namespace директивы. Я заметил, что это также работает без какого-либо пространства имен, но я полагаю, что лучше объявлять перегрузки в том же пространстве имен, что и исходные функции. - person Meindratheal; 06.07.2015

У меня есть следующий пример для вас. Попробуйте использовать следующее:

template<typename E> std::string ToString(const std::vector<E>& t)
{
    std::stringstream resStr;
    for (const E& element : t)
    {
        resStr << element << " ";
    }
    return resStr.str();
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<int> v1;
    v1.push_back(22);
    v1.push_back(33);
    std::vector<double> v2;
    v2.push_back(54.656);
    v2.push_back(44.656);
    auto strInt = ToString(v1);
    auto strDouble = ToString(v2);
    return 0;
}
person arturx64    schedule 05.07.2015

Я использую следующее для векторов базовых типов:

namespace Microsoft
{
    namespace VisualStudio
    {
        namespace CppUnitTestFramework
        {
            template<typename E>
            std::wstring ToString(const std::vector<E> & t)
            {
                std::wstringstream result;
                result << L"Size: " << t.size();

                if (t.size())
                {
                    result << L", Elements: ";

                    for (const auto & element : t)
                    {
                        result << L"{ " << element << L" } ";
                    }
                }
                return result.str();
            }
        }
    }
}

Для неудачных тестов вы можете четко видеть размер вектора И элементы, которые он содержит. Для вашей цели вы можете использовать:

namespace Microsoft
{
    namespace VisualStudio
    {
        namespace CppUnitTestFramework
        {
            template<typename E>
            std::wstring ToString(const std::vector<E> & t)
            {
                return L"Size: " + std::to_wstring(t.size());
            }
        }
    }
}
person Class Skeleton    schedule 26.08.2015