I'm trying to find a way to check whether a lambda can be constant evaluated or not in C++17. Let's assume the lambda I want to check takes no parameters.
I stumbled upon this question, whose accepted answer looks something like this:
template <class Lambda, int = (Lambda{}(), 0)>
constexpr std::true_type is_cx_invocable(Lambda) {
return {};
}
constexpr std::false_type is_cx_invocable(...) {
return {};
}
Which has the following issues:
It only works in C++20 (as requires captureless lambdas to be default constructible) and I'm looking for a C++17 solution;
If
is_cx_invocable
returnsfalse
, it's not clear whether the problem isLambda
not being default constructible or its call operator not beingconstexpr
, e.g.:
int main() {
static_assert(is_cx_invocable([] { return 0; }));
int i{};
auto cxLambda = [i] { return 0; };
(std::array<int, cxLambda()>{}, (void)0);
// ^ cxLambda is indeed constexpr invocable
// (but not default constructible)
auto isCxLambdaCx = is_cx_invocable(cxLambda);
static_assert(!isCxLambdaCx); // This fails
}
So, I tried this instead:
template <class Lambda>
constexpr auto is_cx_invocable(Lambda lambda)
-> std::integral_constant<bool, (lambda(), 1)> { // or
//-> std::enable_if_t<(lambda(), 1), std::true_type> {
return {};
}
constexpr std::false_type is_cx_invocable(...) {
return {};
}
Which:
Doesn't require C++20 features;
Fixes issue (2) above:
int main() {
int i{};
auto cxLambda = [i] { return 0; };
auto isCxLambdaCx = is_cx_invocable(cxLambda);
static_assert(isCxLambdaCx); // OK now
}
The problem is that this solution compiles fine with Clang and MSVC, while failing to compile on GCC. Here you are a Compiler explorer link to play with.
My questions are:
Is my solution ill-formed (so GCC is right in rejecting my code)?
Is there a portable (i.e. implementable with ISO C++ and independent on the compiler quirks) to implement the
is_cx_invocable
check in C++17?