专门用于任意类型向量的模板
Specialise template for vector of arbitrary type
我有一个用于将对象转换为字符串的模板函数(这是来自库,我无法更改它):
template <typename Q> std::wstring ToString(const Q& q) { static_assert(false, "Must specialise"); }
现在,我想用参数 std::vector<Gene>
调用它,其中 Gene
是一个简单的 class,其细节并不重要。当然,要做到这一点,我需要专门化模板,所以我这样做:
template<> std::wstring ToString(const std::vector<Gene>& q){...}
假设我有另一个 class、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
的任意集合?
我看过这个 this question 并尝试像这样声明专业化:
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>
被认为是 "better" 而不是 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());
}
}
}
}
我有一个用于将对象转换为字符串的模板函数(这是来自库,我无法更改它):
template <typename Q> std::wstring ToString(const Q& q) { static_assert(false, "Must specialise"); }
现在,我想用参数 std::vector<Gene>
调用它,其中 Gene
是一个简单的 class,其细节并不重要。当然,要做到这一点,我需要专门化模板,所以我这样做:
template<> std::wstring ToString(const std::vector<Gene>& q){...}
假设我有另一个 class、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
的任意集合?
我看过这个 this question 并尝试像这样声明专业化:
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>
被认为是 "better" 而不是 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());
}
}
}
}