У меня такой код:

#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. По какой причине я не могу использовать 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
        ^~~~~~~~
1
Reza 5 Фев 2020 в 11:05

3 ответа

Лучший ответ

Ни у tuple, ни pair нет присваивания constexpr в C ++ 17.

Но даже тривиальная структура, содержащая пару значений, сделает эту работу. При необходимости вы можете реализовать собственную совместимую с 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 для будущего стандарта.

3
Swift - Friday Pie 5 Фев 2020 в 08:25

Начиная с 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...
    );
}

Вот пример их использования.

0
Bob__ 5 Фев 2020 в 14:16

<{H0}} операторы назначения не являются { {X1}} до с ++ 20. Если вы избегаете операторов присваивания и создаете свои кортежи на месте, тогда ваш код компилируется:

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;
}();
1
Alan Birtles 5 Фев 2020 в 08:25