3

I would like to use a function and pass a constexpr lambda. However, it only compiles successfully if I let the type be deduced through auto. Explicitly giving the type through -> std::array<event, l()> seems to fail (the first instance). Why is this?

template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) -> std::array<event, l()> {
    return {};
} // error

template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) {
    return std::array<event, (l())>{};
} // OK

template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) -> decltype(l()) { return {}; }
// OK

Note that, the lambda returns a size_t.

gcc errors on this without a call (clang accepts it):

prog.cc:9:63: error: template argument 2 is invalid
    9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
      |                                                               ^
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:63: error: template argument 2 is invalid
prog.cc:9:42: error: invalid template-id
    9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
      |                                          ^~~
prog.cc:9:61: error: use of parameter outside function body before '(' token
    9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
  |                                                             ^
prog.cc:9:23: error: deduced class type 'array' in function return type
    9 | constexpr static auto foo(Lambda_T l) -> std::array<event, l()>
  |                       ^~~
In file included from prog.cc:4:
/opt/wandbox/gcc-head/include/c++/9.0.1/array:94:12: note: 
'template<class _Tp, long unsigned int _Nm> struct std::array' declared here
   94 |     struct array
      |            ^~~~~
prog.cc: In function 'int main()':
prog.cc:14:5: error: 'foo' was not declared in this scope
   14 |     foo([]() {return 3; });
      |     ^~~
5
  • 3
    What error do you get? (minimal reproducible example please) Commented Jan 24, 2019 at 18:48
  • 2
    you miss return {}; in first snippet. Commented Jan 24, 2019 at 18:51
  • 1
    Please, a minimal, complete and verifiable example Commented Jan 24, 2019 at 18:57
  • 4
    Compile fine with clang, but not with gcc Demo. Commented Jan 24, 2019 at 19:01
  • 4
    So the crux of the question is whether or or not it is legal to actually use a function parameter in a trailing return type. -> decltype(l()) is fine since the call to l doesn't actually happen, it is just evaluated. -> std::array<event, l()> actually calls l() and since it isn't yet in block scope I don't know if that is legal. Commented Jan 24, 2019 at 19:06

1 Answer 1

4

Parameters to constexpr functions are not themselves constexpr objects - so you cannot use them in constant expressions. Both of your examples returning arrays are ill-formed because there is no valid call to them.

To understand why, consider this nonsense example:

struct Z { int i; constexpr int operator()() const { return i; }; };

template <int V> struct X { };
template <typename F> constexpr auto foo(F f) -> X<f()> { return {}; }

constexpr auto a = foo(Z{2});
constexpr auto b = foo(Z{3});

Z has a constexpr call operator, and this is well-formed:

constexpr auto c = Z{3}();
static_assert(c == 3);

But if the earlier usage were allowed, we'd have two calls to foo<Z> that would have to return different types. This could only fly if the actual value f were the template parameter.


Note that clang compiling the declaration is not, in of itself, a compiler error. This is a class of situations that are ill-formed, no diagnostic required.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.