Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 嵌套模板专门化是如何在C++;_C++_Templates - Fatal编程技术网

C++ 嵌套模板专门化是如何在C++;

C++ 嵌套模板专门化是如何在C++;,c++,templates,C++,Templates,我有一个模板函数定义为: template<typename TObject> TObject Deserialize(long version, const Value &value) 模板对象反序列化(长版本、常量值和值) 我需要做的是编写一个特殊化,它将向量定义为: template<typename TNum, int cnt> class Vec 模板类Vec 并且仍然可以使用cnt和TNum 我试过,但没有成功 template<type

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

template<typename TObject> TObject Deserialize(long version, const Value &value)
模板对象反序列化(长版本、常量值和值)
我需要做的是编写一个特殊化,它将向量定义为:

template<typename TNum, int cnt> class Vec
模板类Vec
并且仍然可以使用cnt和TNum

我试过,但没有成功

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 << "generic\n";
    return {};
}

template<typename T, std::size_t N> 
std::array<T,N> Deserialize_impl(long version, tag<std::array<T,N>>) {
    std::cerr << "special\n";
    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;
}
#包括
#包括
#包括
#包括
#包括


我通常认为这些方法比使用静态方法(这里的另一种主要方法)对结构进行部分专门化更可取,因为函数有很多优点,与专门化相比,它的行为更直观。YMMV.

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

#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 << "generic\n";
    return {};
}

template<typename T, std::size_t N> 
std::array<T,N> Deserialize_impl(long version, tag<std::array<T,N>>) {
    std::cerr << "special\n";
    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;
}
#包括
#包括
#包括
#包括
#包括


我通常认为这些方法比使用静态方法(这里的另一种主要方法)对结构进行部分专门化更可取,因为函数有很多优点,与专门化相比,它的行为更直观。YMMV.

虽然函数标记分派是一种很好的方法,但这里有一个类专门化版本供比较。两者都有各自的用处,我不认为两者都是一个令人遗憾的决定,但也许其中一个更符合你的个人风格。 对于您编写的任何需要自定义反序列化处理程序的类,只需编写反序列化器类的专用化:

#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 impl\n";
        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);
}
#包括
#包括
使用名称空间std;
使用Value=std::string;
//默认反序列化函数
样板
结构反序列化器{
静态ToObject反序列化(长版本、常量值和值){

虽然函数标记分派是一种很好的方法,但这里有一个类专门化版本供比较。两者都有各自的用途,我认为两者都不是一个天生令人遗憾的决定,但也许其中一个更符合您的个人风格。 对于您编写的任何需要自定义反序列化处理程序的类,只需编写反序列化器类的专用化:

#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 impl\n";
        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);
}
#包括
#包括
使用名称空间std;
使用Value=std::string;
//默认反序列化函数
样板
结构反序列化器{
静态ToObject反序列化(长版本、常量值和值){

std::cout理想情况下,在这种情况下,
Vec
应将其自身的模板参数反映为成员
Vec::value\u type
Vec::size()
,其应为
constepr

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

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;
    …
}
模板
结构载体性状;
模板
结构向量特征>{
typedef TNum value_type;
constexpr static int size=cnt;
};
模板TVec反序列化(长版本、值和值){
typedef载体_性状性状;
typedef typename traits::value_type TNum;
constexpr static int cnt=traits::size;
…
}

此解决方案适用于任何现有函数,甚至使签名更干净。此外,该函数更灵活,因为您可以通过添加
traits
专门化而不是整个新的重载来调整它。

理想情况下,
Vec
应将其自身的模板参数反映为成员
Vec::value_键入
Vec::size()
,它们应该是
constepr

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

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;
    …
}
模板
结构载体性状;
模板
结构向量特征>{
typedef TNum value_type;
constexpr static int size=cnt;
};
模板TVec反序列化(长版本、值和值){
typedef载体_性状性状;
typedef typename traits::value_type TNum;
constexpr static int cnt=traits::size;
…
}

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

您不能部分专门化函数。因此,您必须将实际实现隐藏到d的静态方法中ummy template struct,并专门化整个结构。只有一个向量或任何容器?@HolyBlackCat我希望不要这么快给出这个建议。9/10倍的正确答案是重载,而不是转发到结构。函数没有部分专门化,因为它们只是