Title
`std::invoke` forwarding xvalues of incomplete type
Status
new
Section
[meta.trans.other]
Submitter
Jay Ghiron

Created on 2026-10-05.00:00:00 last changed 1 week ago

Messages

Date: 2026-10-13.17:54:31

Consider the following program:

#include <utility>
#include <functional>

extern struct S s;

void f(S&&){}

int main() {
  f(std::move(s));
  std::invoke(f, std::move(s));
}

Assume that `s` is defined in another translation unit. The first call to `f` is clearly defined, and all compilers accept that. However, there is disagreement among implementations about whether or not the second call to `f` is valid when using `invoke`. libstdc++ rejects it, while libc++ and MSVC's STL accept it. This appears to be due to `invoke` referencing `invoke_result` which specifies preconditions that make this undefined. [func.invoke] defines `invoke` as

template<class F, class... Args>
  constexpr invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
    noexcept(is_nothrow_invocable_v<F, Args...>);

[meta.trans.other] has the following precondition on `invoke_result`:

Preconditions: `Fn` and all types in the template parameter pack `ArgTypes` are complete types, cv `void`, or arrays of unknown bound.

`Fn` and `ArgTypes` are `F` and `Args` respectively. In this call to `invoke` `Args` is deduced to have one type which is the incomplete type `S`. Therefore it does not satisfy the precondition. Was it really intended to make this usage of invoke undefined? Note that the following is defined and accepted by all compilers:

#include <utility>
#include <functional>

using T = struct U[];

extern T t;

void g(T&&){}

int main() {
  g(std::move(t));
  std::invoke(g, std::move(t));
}

Assume that `t` is defined in another translation unit. Giving the array type a length causes the implementation disagreement and undefined behavior again.

See also LWG 2939 and others.

History
Date User Action Args
2026-10-05 00:00:00admincreate