问题
I have the following code:
#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;
}();
but this code does not compile due to the std::tuple
. What is the reason I can't use std::tuple
inside a lambda function?
I'm using
c++ -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Wpedantic -std=c++17 -g -o main.o -c main.cpp
to compile the code.
Version of the compiler is: gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)
The error I get is:
../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:
Neither tuple
nor pair
have constexpr assignment in C++17.
But even a trivial struct containing pair of values would do the job. You may want to implement own constexpr-compatible structure if required. Trivial version without fluff you need:
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;
}();
It's possible to arrangle code the way it would use tuple
for future standard
回答2:
std::tuple
's assignment operators are not constexpr
until c++20. If you avoid assignment operators and construct your tuples in-place then your code compiles:
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;
}();
回答3:
Since C++14, std::tuple
's constructors are constexpr
(at least the ones not accepting allocators), so you could use one of those instead of the assignment operator.
Start adding a constexpr
constructor to your class
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} {}
};
Then, you could construct the array using a couple of template functions taking advantage of 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 an example of their usage.
来源:https://stackoverflow.com/questions/60071381/why-cant-i-use-a-stdtuple-in-a-constexpr-lambda-function