Home > Software design >  std::invoke_result<F, Args...> does not seem to give a const type
std::invoke_result<F, Args...> does not seem to give a const type

Time:11-11

I'd like to start with a simplified example:

const bool Foo() { return false; }
bool Bar() { return false; }

int main() {
  std::cout << std::is_same<const bool, std::invoke_result_t<decltype(Foo)>>::value << std::endl;
  std::cout << std::is_same<bool, std::invoke_result_t<decltype(Foo)>>::value << std::endl;
  std::cout << std::is_same<bool, std::invoke_result_t<decltype(Bar)>>::value << std::endl;
}

I expected 1 0 1 but the result was 0 1 1.

Is this expected? If so, is this different from compiler to compiler, or determined by the C standard?

My understanding was we can still return const T but it just does not make much difference/does not make sense since C 11. Thus, I thought the return type is still const T (const bool here) and the compiler would warn me, which does not appear to be the case. I'd like to understand what is going on.

CodePudding user response:

std::invoke_result does not give you the declared return type of the function. It gives you the decltype that an INVOKE expression, i.e. a function call expression in this case, would have with the given argument types. That is specified essentially exactly like this (with a bit more technical wording) in the standard.

A function call expression of a function returning by-value (instead of by-reference) is a prvalue expression. If a prvalue expression is of non-class type, it has its const-qualifier always stripped. There is no difference between a const and non-const non-class prvalue in the language.

So that is why you don't get a const. const on a function returning a non-class type by-value is essentially pointless and compilers will warn you about that with warnings enabled. The only effect the const has here is to change the type of the function. There is no difference in actual calls to the function.

Even const on a class type returned by-value is mostly useless. There are only few special cases where it makes sense (e.g. to disallow calling modifying member functions directly on the call expression), but also comes with significant issues (i.e. potentially disabling move semantics). This was more common in early C , but since C 11 it is also only seldom used.

  • Related