嵌套模板专用化是如何C++完成的

How is nested template specialization done C++

本文关键字:C++ 专用 嵌套      更新时间:2023-10-16

我有一个模板化函数定义为:

template<typename TObject> TObject Deserialize(long version, const Value &value)

我需要做的是编写一个专业化,它将采用定义为:

template<typename TNum, int cnt> class Vec

并且仍然可以使用CNTTNum

我尝试过不成功

template<typename TNum, int cnt> Vec<TNum, cnt> Deserialize<Vec<TNum, cnt>>(long version, Value &value)

导致错误:非法使用显式模板参数

正确的方法是什么?

通常,处理函数模板并需要部分专用化它们的正确答案是简单地重载它们。在这种情况下,这个技巧不能直接工作,因为没有依赖于模板参数的参数,即模板参数是显式指定的而不是推导的。但是,您可以转发到实现函数,并使用简单的标记结构进行重载工作。

#include <functional>
#include <iostream>
#include <type_traits>
#include <vector>
#include <array>
template <class T>
struct tag{};
template<typename TObject> 
TObject Deserialize_impl(long version, tag<TObject>) {
std::cerr << "genericn";
return {};
}
template<typename T, std::size_t N> 
std::array<T,N> Deserialize_impl(long version, tag<std::array<T,N>>) {
std::cerr << "specialn";
return {};
}
template<typename TObject> 
TObject Deserialize(long version) {
return Deserialize_impl(version, tag<TObject>{});
}

int main() {
Deserialize<int>(0);
Deserialize<std::array<int,3>>(0);
return 0;
}

现场示例:http://coliru.stacked-crooked.com/a/9c4fa84d2686997a

我通常发现这些方法比使用静态方法(此处的另一种主要方法)对结构进行部分特化非常可取,因为您可以通过函数利用很多东西,并且与专用化相比,它的行为更直观。扬子晚报.

虽然函数式标签调度是一种不错的方法,但这里有一个类专用化版本进行比较。 两者都有其用途,我不认为任何一个本质上是一个令人遗憾的决定,但也许一个更符合你的个人风格。 对于您编写的任何需要自定义反序列化处理程序的类,只需编写 Deserializer 类的专用化:

#include <iostream>
#include <string>
using namespace std;
using Value = std::string;
// default deserialize function 
template <typename TObject>
struct Deserializer {
static TObject deserialize(long version, const Value &value) {
std::cout << "default impln";
return TObject();
}
};
// free standing function (if you want it) to forward into the classes
template <typename TObject>
TObject deserialize(long version, const Value &value) {
return Deserializer<TObject>::deserialize(version, value);
}
// Stub example for your Vec class
template<typename TNum, int cnt> class Vec { };
// Stub example for your Vec deserializer specialization
template <typename TNum, int cnt> struct Deserializer<Vec<TNum, cnt>> {
static auto deserialize(long version, const Value &value) {
std::cout << "specialization impl: cnt=" << cnt << "n";
return Vec<TNum, cnt>();
}
};
int main() {
Value value{"abcdefg"};
long version = 1;
deserialize<int>(version, value);
deserialize<Vec<int, 10>>(version, value);
}

理想情况下,Vec应将自己的模板参数反映为成员Vec::value_type,并Vec::size()constexpr

如果类无法在自己的接口中提供自己的属性,下一个最好的办法是定义自己的扩展接口。在这种情况下,可以具有单独的元函数(如访问器函数)或特征类(如帮助程序视图类)。我更喜欢后者:

template< typename >
struct vector_traits;
template< typename TNum, int cnt >
struct vector_traits< Vec< TNum, cnt > > {
typedef TNum value_type;
constexpr static int size = cnt;
};
template<typename TVec> TVec Deserialize(long version, Value &value) {
typedef vector_traits< TVec > traits;
typedef typename traits::value_type TNum;
constexpr static int cnt = traits::size;
…
}

该解决方案适合任何现有功能,甚至使签名更清晰。此外,该功能更加灵活,因为您可以通过添加traits专用化而不是全新的重载来调整它。