【问题标题】:constexpr array initialisationconstexpr 数组初始化
【发布时间】:2019-11-05 21:22:32
【问题描述】:

我定义并初始化了一个类似于以下内容的数据结构:

#include <vector>
#include <array>

struct SomeStruct {
    std::vector<int> vec;
};

int main() {
    std::array<SomeStruct, 2> arr {
        SomeStruct {
            .vec = {
                1, 2
            }
        },
        SomeStruct {
            .vec = {
                3, 4, 5
            }
        }
    };
}

这编译正确,但由于整个结构在编译时是已知的,因此我尝试将其设为constexpr。

在前面的示例中简单地将arr 声明为constexpr 会导致错误:

main.cpp: In function ‘int main()’:
main.cpp:20:5: error: the type ‘const std::array’ of constexpr variable ‘arr’ is not literal
     };
     ^
In file included from main.cpp:2:0:
/usr/include/c++/7/array:94:12: note: ‘std::array’ is not literal because:
     struct array
            ^~~~~
/usr/include/c++/7/array:94:12: note:   ‘std::array’ has a non-trivial destructor

我猜这是因为 std::vector 没有 constexpr 构造函数/析构函数。

然后我尝试在包含结构上使用带有模板的std::array:

#include <array>

template <int N>
struct SomeStruct {
    std::array<int, N> vec;
};

int main() {
    constexpr std::array<SomeStruct, 2> arr {
        SomeStruct<2> {
            .vec = {
                1, 2
            }
        },
        SomeStruct<3> {
            .vec = {
                3, 4, 5
            }
        }
    };
}

这也会导致错误:

main.cpp: In function ‘int main()’:
main.cpp:10:39: error: type/value mismatch at argument 1 in template parameter list for ‘template struct std::array’
     constexpr std::array<SomeStruct, 2> arr {
                                       ^
main.cpp:10:39: note:   expected a type, got ‘SomeStruct’
main.cpp:10:41: error: scalar object ‘arr’ requires one element in initializer
     constexpr std::array<SomeStruct, 2> arr {
                                         ^~~

但我不能给 SomeStruct 模板参数,因为大小可能不同。

像这样定义constexpr 数据结构的最佳方法是什么?

【问题讨论】:

  • 您使用的是哪个编译器?我收到 SomeStruct 在 clang 中缺少其模板参数的错误。在 g++ 中,我得到了你所做的错误(所以我假设这就是你使用的),但是我输入了 SomeStruct 的模板参数并且那个错误消失了(但是我在两个元素之间有一个不匹配,因为一个是尺寸 2,另一个是 3)。 godbolt.org/z/lQp3HT
  • @ChrisMM 是的,我正在使用 GCC。我你是对的,我的代码中有一个不同的错误,但是如果不引起你看到的错误就无法解决。更新了问题以澄清。

标签: c++ constexpr


【解决方案1】:

就像说的here

因为std::array&lt;T, N&gt; 是一个聚合,所以当且仅当基础类型T 具有一个constexpr 构造函数(当与您提供的每个初始化程序一起出现时)时,它才能被初始化为一个constexpr。

由于 std::vector 没有 constexpr 构造函数 (yet),这将不起作用。

因此,对于 C++20 之前的版本,这不适用于动态大小的 STL 容器。没有解决方案或快速修复。不要抱太大希望。

另一种方法是设计自己的 constexpr fixed-max-size Vector 类。例如

template <typename T, std::size_t N>
class Vector {
private:
   T values[N]{};
public:
   std::size_t size{ 0 };
   constexpr bool empty() const noexcept { return size == 0; }
   constexpr void clear() noexcept { size = 0; }

   constexpr T* begin() noexcept { return &values[0]; }
   constexpr T* end() noexcept { return &values[size]; }
   constexpr T const* cbegin() const noexcept { return &values[0]; }
   constexpr T const* cend() const noexcept { return &values[size]; }

   constexpr T& back() noexcept { return values[size - 1]; }

   constexpr T operator[] (int const loc) const noexcept { return values[loc]; }
   constexpr T& operator[] (int const loc) noexcept { return values[loc]; }

   constexpr void push_back(T const& value) noexcept { values[size++] = value; }

   constexpr void resize(int const newSize) noexcept {
       auto const diff = newSize - size;
       if (diff > 0) memset(end(), 0, diff * sizeof(T));
       size = newSize;
   }
};

这是我有时使用的...你需要添加一个initializer_list 构造函数。

编辑:快速测试...这似乎可以编译。

#include <array>

template <typename T, std::size_t N>
class Vector {
private:
   T values[N]{};
public:
    std::size_t size{ 0 };
    constexpr Vector(std::initializer_list<T> il) noexcept
     : size(std::distance(std::cbegin(il), std::cend(il)))
     {
         std::size_t i = 0;
         for(auto it = std::cbegin(il); it != std::cend(il); ++it) {
             values[i++]=*it;
         }
     }
};

struct SomeStruct {
    Vector<int,5> vec;
};

int main() {
    [[maybe_unused]]constexpr std::array<SomeStruct, 2> arr {
        SomeStruct {
            .vec = {
                1, 2
            }
        },
        SomeStruct {
            .vec = {
                3, 4, 5
            }
        }
    };
}

【讨论】:

  • 我知道这行不通,我在我的问题中解决了这个问题。我正在寻找将起作用的建议。
  • @Flau 哇哦!只是想帮忙……有时现实令人失望。更新了我的答案。
  • 对不起,有人已经发布并删除了一个答案,告诉我 vector 没有 constexpr 构造函数;感谢您的更新。
  • 据this list报道,C++20尚未采用constexpr向量论文。
  • 接受这一点,因为它保持使用语法相同。很遗憾,目前没有更好的方法来做到这一点(使用 std::array 或其他方式),但希望 C++20 能对此有所帮助。
【解决方案2】:

如果您单独存储行,那么您可以拥有一个跨度数组。您必须使行至少与父数组一样长。在这个例子中,它们是同一范围内的变量:

struct SomeStruct {
    std::span<int> vec;
};

std::array arr1 {1, 2};
std::array arr2 {3, 4, 5};
constexpr std::array arr {
    SomeStruct { .vec = arr1 },
    SomeStruct { .vec = arr2 },
};

或者,具有更多 constexpr 的非可变版本:

struct SomeStruct {
    std::span<const int> vec;
};

constexpr std::array arr1 {1, 2};
constexpr std::array arr2 {3, 4, 5};
constexpr std::array arr  {
    SomeStruct { .vec = arr1  },
    SomeStruct { .vec = arr2  },
};

【讨论】:

  • 嗯...std::span 是 C++20,所以没有正式发布...这不适用于 C++17...加上 span 将是一个参考类/查看器,所以如果arr1 和/或arr2 被销毁,arr 将失效。或者我不理解它。这就是 C++20 的问题……没有正式出局,所以……
  • @JHBonarius 指定的初始化列表也是 C++20,OP 使用这些。您始终可以使用span 的非标准实现来代替;它可以在没有 C++20 的情况下实现。您必须使行至少与父数组一样长。
  • @JHBonarius 是的,我在没有测试的情况下添加了一些 constexpr 是我的错。仅当成员 span 为 const int 时,行才能为 constexpr。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 1970-01-01
  • 2019-12-14
  • 2012-11-10
相关资源
最近更新 更多