1

目标是将大型数据结构序列化为constexpr初始化,因此它们将成为.textor.rodata段的一部分,并可用于constevalconstexpr

数据结构包含用于交叉引用/索引等的容器。所有这些代码都将生成,因此我们可以使用相同或等效的容器逻辑在代码生成器内计算容器状态,然后按原样序列化容器状态。

C++ 不支持constexpr.

“在 Kona 2019 中,这种方法被认为过于脆弱,因此非瞬态分配被从功能集中移除。” http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p0784r7.html

因此,使用std::或自制动态容器是不可能的。

(可变参数?)模板的使用似乎也是不可能的,因为数据结构必须以运行时动态方式可用,即任何模板的使用都需要在顶部有一个类型擦除层,这看起来很笨拙。见https://www.artima.com/articles/on-the-tension-between-object-oriented-and-generic-programming-in-c

正如这里所探讨的,下一次尝试是尝试序列化为复合文字。请参阅https://en.cppreference.com/w/c/language/compound_literal

标准 C++20 实际上并没有正式支持复合文字,但我们可以依靠编译器扩展来支持 C++ 中的这个 C 特性。似乎在所有主要的 C++ 编译器中都可用。

下面的示例只是一个非常小的概念证明(或尝试这样做)。

问题:以下适用于 Clang 但不适用于 GCC。为什么?有不同的方法吗?

#include <memory>

template <typename K, typename V>
struct Entry{
    K key;
    V value;
};

template <typename K, typename V>
struct Bucket{
    size_t n;
    // C++ cannot initialize a flexible array in a struct, use a pointer.
    Entry<K, V> *entries; 
};

template <typename K, typename V>
struct HashMap{
    using bucket_t = Bucket<K, V>;
    using entry_t = Entry<K, V>;
    size_t n;
    // C++ cannot initialize a flexible array in a struct, use a pointer.
    bucket_t *buckets; 

    constexpr V get(K key) const {
        const size_t i = key % n;
        const auto &bucket = buckets[i];
        for (size_t j = 0; j < bucket.n; ++j) {
            if (bucket.entries[j].key == key) {
                return bucket.entries[j].value;
            }
        }
        return {};
    }
};

using MyHashMap = HashMap<int, double>;
constexpr MyHashMap map =
{
        2,
        // Because we can't initialize a flexible array, we need to employ a so-called 
        // compound literal and let it decay to a pointer. 
        (MyHashMap::bucket_t[]) 
        {
                { 
                    3, 
                    (MyHashMap::entry_t[]) // dito
                    { {0, 1.2}, {2, 2.4}, {6, 3.6} } 
                },
                { 
                    4, 
                    (MyHashMap::entry_t[]) // dito
                    { {1, 0.0}, {5, 2.0}, {11, 4.0}, {13, 1.0} } 
                }
        }
};

// Serves as proof of concept by succeeding as consteval.
consteval int proofOfConcept() {
    // Should return 76 as a constant, 
    return 10*(map.get(0) + map.get(11) + map.get(2));
}

int main() {
    return proofOfConcept();
}

在 GCC 上,这失败了。

<source>: In function 'int main()':
<source>:94:26:   in 'constexpr' expansion of 'proofOfConcept()'
<source>:90:23:   in 'constexpr' expansion of 'map.HashMap<int, double>::get(0)'
<source>:94:26: error: the value of '._anon_115' is not usable in a constant expression
   94 |     return proofOfConcept();
      |            ~~~~~~~~~~~~~~^~
<source>:84:9: note: '._anon_115' was not declared 'constexpr'
   84 |         }
      |         ^

在这里探索它:

https://godbolt.org/z/nMKnoY6fY

4

1 回答 1

0

正如康桓玮所指出的,这是一生的问题。似乎每个嵌套数组文字都需要单独定义为一个符号,然后在其位置使用。这也避免了非标准复合文字的使用。

但它增加了传递常数的问题。我们需要能够用 constness 参数化包含结构。

所以这似乎是一个解决方案:

#include <memory>

template<bool isConst, typename T>
struct TransitiveConst { using Result = T; };

template<typename T>
struct TransitiveConst<true, T> { using Result = const T; };

template <typename K, typename V>
struct Entry{
    K key;
    V value;
};

template <typename E>
struct Bucket{
    size_t n;
    // C++ cannot initialize a flexible array in a struct, use a pointer.
    E *entries;
};

template <bool isConst, typename K, typename V>
struct HashMap{
    using entry_t = typename TransitiveConst<isConst, Entry<K, V>>::Result;
    using bucket_t = typename TransitiveConst<isConst, Bucket<entry_t>>::Result;
    size_t n;
    // C++ cannot initialize a flexible array in a struct, use a pointer.
    bucket_t *buckets;

    constexpr V get(K key) const {
        const size_t i = key % n;
        const auto &bucket = buckets[i];
        for (size_t j = 0; j < bucket.n; ++j) {
            if (bucket.entries[j].key == key) {
                return bucket.entries[j].value;
            }
        }
        return {};
    }
};

using MyHashMap = HashMap<true, const int, const double>;

constexpr MyHashMap::entry_t entry0[] = { {0, 1.2}, {2, 2.4}, {6, 3.6} };
constexpr MyHashMap::entry_t entry1[] = { {1, 0.0}, {5, 2.0}, {11, 4.0}, {13, 1.0} };
constexpr MyHashMap::bucket_t buckets[] = {
        {
            std::size(entry0),
            entry0
        },
        {
            std::size(entry1),
            entry1
        }
};

constexpr MyHashMap map =
{
        std::size(buckets),
        buckets
};

// Serves as proof of concept by succeeding as consteval.
consteval int proofOfConcept() {
    // Should return 76 as a constant,
    return (10*(map.get(0) + map.get(11) + map.get(2)));
}

int main() {
    return proofOfConcept();
}

https://godbolt.org/z/fbfTdGxE7

于 2021-05-11T15:26:35.480 回答