The wording of the question title is probably incorrect and I'll fix it happily from your suggestions.
My question can be illustrated by this snippet:
#include <array>
template <typename Callable>
constexpr auto make_array_ok(Callable callable) {
return std::array<int, callable()>{};
};
// constexpr auto make_array_bad(std::size_t s)
// {
// return std::array<int,s>{};
// };
// straightforward constexpr function returning its arg
template <std::size_t S>
constexpr std::size_t foo() {
return S;
}
int main(int argc, char**) {
static_cast<void>(argc);
auto size = []() { return std::size_t{42}; };
// fails to compile -- as I expected
// auto size_dyn = [argc]() { return std::size_t(argc); };
// [[maybe_unused]] auto a = make_array_ok(size_dyn);
// also fails to compile -- but why?
[[maybe_unused]] auto size_capt = [arg = size()]() { return arg; };
// [[maybe_unused]] auto b = make_array_ok(size_capt);
// also fails to compile -- but why?
[[maybe_unused]] constexpr auto size_capt_ce = [arg = size()]() {
return arg;
};
// [[maybe_unused]] auto c = make_array_ok(size_capt_ce);
// direct usage also fails to compile -- is it because the closure is not
// constexpr? auto d = std::array<int,size_capt()>{}; direct usage compiles
// when the closure is constexpr
[[maybe_unused]] auto e = std::array<int, size_capt_ce()>{};
// Simpler exemple with a constexpr function instead of a lambda closure.
[[maybe_unused]] auto f = std::array<int, foo<42>()>{};
// calling the constexpr function through a function fails to compile - but
// why?
// [[maybe_unused]] auto g = make_array_ok(foo<42>);
// compiles with captureless lambda
[[maybe_unused]] auto h = make_array_ok(size);
return size_capt_ce();
}
It's quite common that constexpr function arguments are considered as not usable where a constant expression is expected (as constexpr function can be called at runtime, with runtime argument), so the failure of make_array_bad
to compile is expected. But under what rules can make_array_ok
compile (when used with the lambda returning 42
)?
I'm suspecting that it's because lambda's operator()
is constexpr by default from C++17 on, but I didn't find details in cppreference about constexpr function about the usability of its argument in constant expressions.
But then why does it not work with the size_capt
version?
[EDIT] I updated the example above with a constexpr
function instead of a lambda and showing the difference between a direct usage and an usage through a function call. I hope it helps clarifying the issue and can be used to improve the (already interesting) provided answers.
Is it the callable
copy (when passed as argument) that is breaking the requirements for constant expression?
constexpr
and the second isn't – Sollieconstexpr
if the function call operator (or specialization, for generic lambdas) is constexpr." ? The lambda, if capture-less is implicitly convertible to a pointer toconstexpr
function? – Harness