这是为成员函数的 C++ 模板部分特化构造类的好方法吗?

问题描述

我正在通过一系列练习自学 C++。 我喜欢仅使用该语言就可以完成哈希表的想法 并且没有 std 调用。我发现你做不到 “类成员函数部分模板专业化”等工作的选项 这将如何实现。 下面的代码是哈希表有用部分的精简版本。 留下足以显示所需内容的结构。

有没有更好的方法来构造类 - IE 是解决偏特化的更好的习惯用法

我尝试过全类特化和结构部分模板特化 但这似乎是目前最好的。

我对其他调用成员和函数的方式感兴趣。 我想到了:

  1. 功能中所有成员/功能的“使用”行。
  2. 一行 'using' 用于创建可用作前缀的 typedef。
  3. 每次使用时都使用完整的演员表。
#include "stdio.h"

template <typename K,typename H>
class hash_table_common {
public:
    H& hash_type()                          // Standard CRTP helper function
    {
        return static_cast<H&>(*this);
    }
    int& operator[](K key)
    {
        return hash_type().get_value(key);
    }
    size_t hash(int key)
    {
        return key % 10;
    }
    int m_storage[10]{ 0 };
};

template <typename K>
class hash_table : public hash_table_common<K,hash_table<K>> {
public:
    class hash_table_common<K,hash_table<K>>& hash_type()
    {
        return static_cast<hash_table_common<K,hash_table<K>>&>(*this);
    }
    int& get_value(K key)
    {
        int hashable = 3; // value_to_int(); Real code will go here,for this demo it works for one value!
        int index1 = hash_type().hash(hashable);
        return hash_type().m_storage[index1];
    }
};

template <>
class hash_table<const char*> : public hash_table_common<const char*,hash_table<const char*>> {
public:
    class hash_table_common<const char*,hash_table<const char*>>& hash_type()
    {
        return static_cast<hash_table_common<const char*,hash_table<const char*>>&>(*this);
    }
    int& get_value(const char* key)
    {
        int hash_as_int = (int)key[0];
        int index = hash_type().hash(hash_as_int);
        return hash_type().m_storage[index];
    }
};
#endif

int main() {
    class hash_table<const char*> a;
    class hash_table<float> b;
    a["word"] = 3;
    b[4.5f] = 14;
    printf("%d %d",a["word"],b[4.5f]);
    return 0;
}

解决方法

首先,让我们考虑您实现哈希表的示例。我对你的主要内容做了一些小的修改:

int main() {    
    my_hash_table<const char*> c;
    my_hash_table<float> d;
    c["word"] = 3;
    d[4.5f] = 14;
    std::cout << c["word"] << " " << d[4.5f] << "\n";
}

让我们将此作为我们想要通过的测试用例。

忽略实现的缺陷,只谈设计,这是“通过测试”并专门化const char*float所需要的:

#include <map>
#include <iostream>
#include <type_traits>

template <typename T>
struct my_hash {
    size_t operator()(const T& t){ return t;}
};
template <typename key_t,typename hash = my_hash<key_t>,typename value_t = int>
struct my_hash_table {
    std::map<size_t,value_t> table;
    value_t& operator[](key_t key){ return table[hash{}(key)]; }
};
template <>
struct my_hash<const char*> {
    size_t operator()(const char* t){ return t[0]; }
};
template <>
struct my_hash<float> {
    size_t operator()(const float& t) { return t*100; }
};

不要误会我的意思,但是您的代码看起来像是试图将所有内容都压缩到一个类中。并非与类相关的所有内容都必须在类的定义中。相反,类的定义越少越好。函子通常相当轻量级,它们有一个 operator()(即它们可以被调用)并且通常就是这样。您可以轻松地为 my_hash 添加专业化。

此外,继承也有它的用处,但老实说,我不明白它在您的代码中的作用是什么。最后,但并非最不重要的是,您将散列函数与容器混合在一起,但这些是单独的问题。不同的容器可能使用相同的散列函数,而您可能希望使用具有不同散列的相同数据结构。

“类成员函数部分模板特化”不是一回事。不过,这本身并不构成问题。它只是我们盒子里没有的工具。当您希望类模板的成员函数仅根据其中一个参数执行某些特殊操作时,您可以这样做。要么如上,要么 C++17 引入了 if constexpr :

template <typename A> struct foo { void operator()() {} };
template <> struct foo<int> { void operator()(){ std::cout << "A is int\n";}};

template <typename A,typename B>
struct bar {
    void do_something() {
        
        foo<A>{}();

        if constexpr (std::is_same_v<A,int>){
            std::cout << "A is int\n";
        }
    };
};

Live Example

,

使用@largest 的结构方法,这是此用例的最佳习语的另一个候选解决方案。我想我仍然喜欢 CRTP 选项,因为它以对称方式调用其他部分函数和成员。 "scripts": { "vscode:prepublish": "webpack --mode production","webpack": "webpack --mode development","webpack-dev": "webpack --mode development --watch","test-compile": "tsc -p ./","compile": "tsc -b","watch": "tsc -b -w","postinstall": "cd client && npm install && cd ../server && npm install && cd ..","test": "sh ./scripts/e2e.sh" },

在下面的代码中,hash_type()hash_table_functions 必须有更好的名称 因为这些应该是不可见的重定向,所以我想说 table_{},但下划线警察会追上我(笑)。

_
,

仅适用于 C++17 的 constexpr 版本。模拟 is_same_v 以保持原始规则。这很有用,因为它教会了我这一切是如何运作的。 这会使代码更清晰、更易于阅读。

// C++ 17 required
#include <stdio.h>
#include <stdint.h>

template<class type1,class type2>
struct is_same
{
    static constexpr bool _value = false;
    constexpr operator bool() const noexcept { return _value; }
};

template<class type1>
struct is_same<type1,type1>
{
    static constexpr bool _value = true;
    constexpr operator bool() const noexcept { return _value; }
};

template<class type1,class type2>
inline constexpr bool is_same_v = is_same<type1,type2>::_value;

template <typename key_t>
class hash_table {
public:
    int& operator[](key_t key)
    {
        return get_value(key);
    };
    size_t hash(int key)
    {
        return key % 10;
    }
    int m_storage[10]{ 0 };
    int& get_value(key_t key)
    {
        uint32_t hashable;
        if constexpr ( is_same_v<key_t,const char*> )
        {
            hashable = (uint32_t)key[0];
        }
        else
        {
            hashable = (uint32_t)key;
        }
        size_t index = hash(hashable);
        return m_storage[index];
    }
};

int main()
{
    class hash_table<const char*> a;
    class hash_table<float> b;
    a["word"] = 3;
    b[4.5f] = 14;
    printf("%d %d",a["word"],b[4.5f]);
    return 0;
}