【问题标题】:c++ compile-time associative arrayc++ 编译时关联数组
【发布时间】:2020-09-03 14:30:29
【问题描述】:

我认为这个问题可能更多地是关于我对模板做任何不平凡的事情的弱点,而不是问题本身,但我想实现一个这样使用的模板类:

MyArray<std::string, 1, 3, 7> a;
a[1] = "hello";
a[3] = "world";
a[7] = "!";

// runtime error
// a[4] = "?";

在底层,MyArray&lt;std::string, 1, 3, 7&gt; 基本上等同于以下内容:

class C
{
  private:

    std::string _values[3];

  public:

    std::string & operator[](std::size_t index)
    {
      switch(index)
      {
        case 1: return _values[0];
        case 3: return _values[1];
        case 7: return _values[2];
        default: /* some error condition */
      }
    }
};

(当然,在现实生活中,我更可能使用枚举值而不是原始整数,但这用最少的示例代码就可以理解。)

标准:

  • 如果索引在编译时未知,则需要工作
  • 访问应该使用 switch 语句或等效的语句,而不是通过允许的键进行运行时线性搜索。
  • 不想要一个像std::unordered_map这样的有潜在桶冲突的hashmap

我也很高兴有一个解释如何编写这种模板代码的参考。

【问题讨论】:

  • 关于您的第一点:您是指来自MyArray&lt;std::string, 1, 3, 7&gt; 的可用索引还是来自例如请求的索引a[1] = "hello";?
  • @Quentin:我的意思是像 int i; std::cin &gt;&gt; i; std::cout &lt;&lt; a[i] 这样的东西应该可以编译。
  • 实现 switch case 很简单,但这是一个线性搜索。您在寻找完美的哈希吗?
  • 没有线性要求...您可以使用std::map,通过构造函数创建所需的值,并在运行时抛出异常,通过对应于非当前值的索引访问地图.问题是std::map 是(如果我没记错的话)O(n log(n))
  • @AtnNn:我希望是这样,但看起来至少 g++ 会很高兴地为等效的 if/elseif/else 块生成一个巨大的线性搜索:godbolt.org/z/ecTz19跨度>

标签: c++ templates


【解决方案1】:

好的,如果您不介意可能的巨大空间成本,您可以这样做:

template<typename T, size_t Head, size_t... Tails>
class MagicArray{
    std::array<T, 1 + sizeof...(Tails)> data;
    static constexpr size_t max(size_t a, size_t b) noexcept{
        return (a < b) ? b : a;
    }
    template<typename head, typename... tails>
    static constexpr size_t max(head a, tails... b) noexcept{
        if constexpr (sizeof...(b) == 0)
            return a;
        else
            return max(a, max(b...));
    }
    static constexpr size_t value_max = max(Head, Tails...);
    template<size_t addition, size_t I, size_t A, size_t... B>
    static constexpr size_t find() noexcept{
        if constexpr (I == A)
            return addition;
        else if constexpr (sizeof...(B) == 0)
            return value_max;
        else
            return find<addition + 1, I, B...>();
    }
    template<size_t... Is>
    static constexpr std::array<size_t, value_max + 1> generation(std::index_sequence<Is...>*) noexcept{
        return { (find<0, Is, Head, Tails...>())... };
    }
    static constexpr std::array<size_t, value_max + 1> mapping = generation((std::make_index_sequence<value_max + 1>*)nullptr);
public:
    T& operator[](size_t index){
        if (index > value_max || mapping[index] == value_max)
            throw 0;
        else
            return data[mapping[index]];
    }
    T const& operator[](size_t index) const{
        if (index > value_max || mapping[index] == value_max)
            throw 0;
        else
            return data[mapping[index]];
    }
};

int main(){
    MagicArray<std::string, 1, 3, 7> a;
    a[1] = "Hello";
    a[3] = "World";
    a[7] = "!";
    a[9] = "Boooooooooooooooom!";
}

【讨论】:

  • 我开始看到我想要的东西是不可能的(除了使用宏而不是我宁愿避免的模板),这实际上适用于我的用例(也许有如果事情变得太稀疏,则转移到 std::unordered_map ),所以我接受它。感谢您的建议!
猜你喜欢
  • 1970-01-01
  • 2023-01-25
  • 1970-01-01
  • 2010-12-27
  • 1970-01-01
  • 2016-11-26
  • 2020-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多