I'm trying to use a constexpr constructor in C++17 with a lambda that uses std::tie
to initialize fields in a class from a tuple.
The code is similar to this:
#include <tuple>
enum class Format {
UINT8,
UINT16
};
struct FormatInfo {
const char* name = nullptr;
int maxVal = 0;
constexpr explicit FormatInfo(Format fmt) {
auto set = [this](const auto&... args) constexpr {
std::tie(name, maxVal) = std::make_tuple(args...);
};
switch(fmt) {
case Format::UINT8: set("uint8", 255); break;
case Format::UINT16: set("uint16", 65535); break;
}
}
};
int main() {
FormatInfo info(Format::UINT8); // ok
constexpr FormatInfo info2(Format::UINT8); // fails
}
Calling the constructor as constexpr fails, with an error that there is a call to a non-constexpr function inside set
. Even though both std::tie
and std::make_tuple
should be constexpr.
Making the lambda itself constexpr (constexpr auto set = ...
) also fails with an error that this
is not a constant expression.
Is there any way to make this work in C++17?
std::tie
returns a tuple of reference types. std::tuple<...>::operator=
is not marked constexpr
in C++17.
You can emulate what operator=
does with other (constexpr
) functions:
auto set = [this](const auto&... args) {
std::apply([&](auto&... tied) {
(void(tied = args), ...);
}, std::tie(name, maxVal));
// std::tie(name, maxVal) = std::make_tuple(args...);
};
Or you can ditch the tuples:
auto set = [this](const auto& name, const auto& maxVal) {
this->name = name;
this->maxVal = maxVal;
};