У меня есть функция шаблона для преобразования объекта в строку (это из библиотеки, я не могу изменить это):
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, a 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, а исходная функция шаблона происходит из собственной тестовой библиотеки.
Вы почти все сделали правильно, но в случае функций вы не должны специализировать их, вы должны просто перегрузить их так:
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.
У меня есть следующий пример для вас. Попробуйте использовать следующее:
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;
}
Я использую следующее для векторов базовых типов:
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());
}
}
}
}