Created on 2026-10-05.00:00:00 last changed 1 week ago
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:00 | admin | create | |