【问题标题】:Why can't I use a std::tuple in a constexpr lambda function为什么我不能在 constexpr lambda 函数中使用 std::tuple
【发布时间】:2020-02-05 08:05:09
【问题描述】:

我有以下代码:

#include <string_view>
#include <array>
#include <tuple>

struct Variable
{
  size_t index;
  std::string_view name;
  std::tuple<float, float> bounds;
};

constexpr std::array<Variable, 3> myarray = [](){
    std::array<Variable, 3> res{};
    std::array<std::string_view, 3> strings = {"myvar1", "myvar2", "myvar3"};
    std::array<std::tuple<float, float>, 3> bounds = {{{0,1}, {1,2}, {2,3}}};

    for (std::size_t i = 0; i != res.size(); ++i) {
        res[i] = {i, strings[i], bounds[i]};
    }
    return res;
}();

但由于std::tuple,此代码无法编译。我不能在 lambda 函数中使用 std::tuple 的原因是什么?

我正在使用

c++ -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -o main.o -c main.cpp

编译代码。

编译器版本为:gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)

我得到的错误是:

../main.cpp:53:3: error: call to non-constexpr function ‘<lambda()>’
 }();
   ^
../main.cpp:44:51: note: ‘<lambda()>’ is not usable as a constexpr function because:
 constexpr std::array<Variable, num_vars> xrt = [](){
                                               ^
../main.cpp:51:39: error: call to non-constexpr function ‘Variable& Variable::operator=(Variable&&)’
     res[i] = {i, strings[i], bounds[i]};
                                   ^
../main.cpp:16:8: note: ‘Variable& Variable::operator=(Variable&&)’ is not usable as a constexpr function because:
 struct Variable
        ^~~~~~~~

【问题讨论】:

  • 你用的是什么编译器?它的什么版本?编译时你给编译器什么标志或选项?你得到什么错误?为什么你认为问题出在std::tuple?最后,在创建minimal reproducible example 时,请尽量确保它不包含任何其他不相关的错误(例如缺少分号)。
  • 根据clang,问题出在:res[i] = {i, strings[i], bounds[i]};
  • 你忘了;在变量声明之后。还要确保包含&lt;string&gt;&lt;string_view&gt; 可能只是前向声明它。
  • 元组赋值运算符在 c++20 之前不是 constexpr:en.cppreference.com/w/cpp/utility/tuple/operator%3D

标签: c++ c++17 stdtuple


【解决方案1】:

tuplepair 在 C++17 中都没有 constexpr 赋值。

但即使是包含一对值的微不足道的结构也可以完成这项工作。如果需要,您可能希望实现自己的 constexpr 兼容结构。您需要的没有绒毛的简单版本:

struct Couple {
  float a, b;  
};

struct Variable
{
  size_t index;
  std::string_view name;
  Couple bounds;
};

constexpr std::array<Variable, 3> myarray = [](){
    std::array<Variable, 3> res{};
    std::array<std::string_view, 3> strings = {"myvar1", "myvar2", "myvar3"};
    std::array<Couple, 3> bounds = {{{0,1}, {1,2}, {2,3}}};

    for (std::size_t i = 0; i != res.size(); ++i) {
        res[i] = {i, strings[i], bounds[i]};
    }
    return res;
}();

可以按照将tuple 用于未来标准的方式排列代码

【讨论】:

  • 你能澄清一下你的最后一句话吗?
  • @Reza 类似#if __cplusplus &gt; 201703L \ template &lt;class ...Args&gt; using Couple = std::tuple&lt;Args...&gt;; \ # else \ /*Your definition of Couple */
【解决方案2】:

std::tupleassignment operators 在 c++20 之前不是 constexpr。如果您避免赋值运算符并就地构造元组,那么您的代码将编译:

constexpr std::array<Variable, 3> myarray = [](){
    std::array<std::string_view, 3> strings = {"myvar1", "myvar2", "myvar3"};
    std::array<std::tuple<float, float>, 3> bounds = {{{0,1}, {1,2}, {2,3}}};

    std::array<Variable, 3> res { {
        {0, strings[0], bounds[0]},
        {1, strings[1], bounds[1]},
        {2, strings[2], bounds[2]}
    } };
    return res;
}();

【讨论】:

  • 初始循环的想法是强制执行res[i].index == i
  • @Jarod42 是的,但只有 3 个元素很容易看出它是正确的
  • 3 个元素不需要整个 lambda ;-)
【解决方案3】:

从 C++14 开始,std::tuple 的构造函数是 constexpr(至少是不接受分配器的构造函数),因此您可以使用其中之一来代替赋值运算符。

开始向您的类添加constexpr 构造函数

struct Variable
{
    size_t index{};
    std::string_view name{};
    std::tuple<float, float> bounds{};

    constexpr Variable(size_t i, std::string_view str)
        : index{i}, name{str}, bounds{i, i + 1} {}
};

然后,您可以利用std::integer_sequence 使用几个模板函数来构造数组。

template <class Element, std::size_t... I, typename... ArgsType>
constexpr auto make_array_with_indices_impl(std::index_sequence<I...>, ArgsType... args)
{
    return std::array<Element, sizeof...(args)>{
        Element(I, args)...
    };    
}

template <class Element, typename... ArgsType>
constexpr auto make_array_with_indices(ArgsType... args)
{
    return make_array_with_indices_impl<Element>(
        std::index_sequence_for<ArgsType...>{}, args...
    );
}

Here 的用法示例。

【讨论】:

    猜你喜欢
    • 2017-05-02
    • 2020-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 2020-12-01
    • 2014-08-31
    相关资源
    最近更新 更多