What was the problem with std::is_callable?

122 views Asked by At

Since 2017-03-13 std::is_callable is gone from cppreference.com. The last available description of it is from 2016-11-21 on WaybackMachine.

The main difference between std::is_callable and std::is_invocable, that replaced it, is that

  • the former used a single template parameter template <class FnArgs> specialized to FnArgs = Fn(Args...) for the callable type Fn and for the parameter types to test (Args...), while
  • the latter accepts all of these in separate template parameters (template <class Fn, class... Args>).

What was the problem with std::is_callable's Fn(Args...) approach?

I understand, that Fn(Args...) is a function type, where Fn is the return type. std::is_callable gave Fn another meaning, the function type to test, which I find misleading. This is only one problem. Could you name all the rest?

What I can think of, but cannot put it together (in keywords):

  • Not every type can be return or parameter type of a function, e.g. void, abstract classes, C arrays, function types, abominable function types, incomplete types come to mind.
  • In function parameters array and function types decay into pointers (C++23 [dcl.fct]#5), and the top-level cv-qualifiers are discarded (C++23 [dcl.fct]#5).
  • I don't know, maybe the Fn(Args...) approach may introduce ambiguity in some cases.
  • Changing Fn(Args...) to separate template parameters was proposed in LWG2895.

I have found one compiler on Godbolt, on which std::is_callable works: MVSC v19.10. The others say there is no such type in namespace std. Seeing it work helps to understand this type trait more deeply, e.g. Alisdair Meredith's example from P0604r0. See on Godbolt.

1

There are 1 answers

0
user12002570 On

What was the problem with std::is_callable's Fn(Args...) approach?

This can be answered from P0604 (which renamed is_callable) bullet 3 that states:

  1. Replace function type encoding of type lists in is_callable and result_of:

The function type encoding form used in these type traits is a relict from the pre-variadic template era of C++ where a single type is used to express a list of types. First, for both traits the natural interpretation of the encoded form Fn(ArgTypes...) looks as if Fn is used as return type, but it is only the callable object to which the arguments are provided. Second, contrary to the rest of all type traits which use a glvalue-based expression approach due to the direct usage of declval applied to the given type parameters, function type encoding is more natural for non-glvalues. In particular, parameter types are first adjusted via a special decay-like mechanism (8.3.5 [dcl.fct]), such that the actually provided types are often not the types on which the actual test is applied. Another problem is that function return types and argument types prevent some important type classes to be actually provided in the encoded form: Neither can argument types have an abstract class type nor cv void type, nor can function types or array types be used as "return type".