Skip to content

[concepts.callable.general, concept.invocable] add function types and references#8402

Closed
morinmorin wants to merge 1 commit into
cplusplus:mainfrom
morinmorin:fix_callable_concepts
Closed

[concepts.callable.general, concept.invocable] add function types and references#8402
morinmorin wants to merge 1 commit into
cplusplus:mainfrom
morinmorin:fix_callable_concepts

Conversation

@morinmorin

Copy link
Copy Markdown
Contributor

This is a follow-up to PR #8255 (and fixes Issue #8241).

In [concepts.callable.general] and [concept.invocable], it would be more appropriate to use "function types, callable types, and references to such types" instead of "callable types".

@eisenwave

eisenwave commented Nov 4, 2025

Copy link
Copy Markdown
Member

Did you consider extending the definition of "callable types" to include function references as well? My intuition for the status quo is that a "callable type" is anything that can be thrown into invoke, i.e. INVOKE. While that's not technically true right now, maybe it should be true.

What you're proposing doesn't seem compelling from a wording cost/benefit perspective because it's all intro fluff wording anyway. We don't need to be super precise there.

@frederick-vs-ja

frederick-vs-ja commented Nov 4, 2025

Copy link
Copy Markdown
Contributor

Instead, I think it would make more sense to modify [func.def] as

[...]
3. A callable type is a function type, function object type ([function.objects]) or a , pointer to member type, or reference to such a type.
[...]

And p4 doesn't need to be modified because there's no object of a function or reference type.

@morinmorin

Copy link
Copy Markdown
Contributor Author

Thank you both for your insightful feedback!

When I first read the wording on "18.7 callable concepts", I assumed that callable types included function types as well. After re-reading, it appears that's not the case, so I've opened an issue.
I agreed that tweaking the introductory wording is not compelling; adjusting the definition of callable type would indeed be a proper fix.

Including function types in callable type would be a non-editorial change, so we could take this to LWG as an issue. I'll close this PR.

@morinmorin morinmorin closed this Nov 4, 2025
@morinmorin

Copy link
Copy Markdown
Contributor Author

As an aside, I'd like to note a related thought about function types and the callable concepts, specifically regular_invocable.

Imagine a hypothetical world where a compiler could magically check the semantic requirements of regular_invocable. Let F be the function type int(int) (i.e. using F = int(int);) and consider

int square(int x) { return x * x; }
int random(int x) { return std::random_device{}() % x; }

In that world, square would satisfy the requirements of regular_invocable, whereas random would not. Then, what should regular_invocable<F, int> evaluate to? I guess we're stuck with false in practice.

@eisenwave

eisenwave commented Nov 4, 2025

Copy link
Copy Markdown
Member

https://eel.is/c++draft/res.on.requirements#2 The program would be IFNDR if you used regular_invocable with a type that satisfies but does not model that concept. Of course, that seems a bit dumb if we consider regular_invocable<int(), int>, where int() could be any one of infinitely many possible functions.

The specification isn't clear on that, but these semantic requirements are situational. My understanding is that e.g. it is well-formed to use std::sort on a std::vector<float> as long as there are no NaNs in that std::vector. If there were NaNs, the < operator wouldn't give you a strict weak ordering. The strict weak ordering requirement is tied to the specific invocation of std::sort with that specific std::vector; it's not global.

In short, the "satisfies" part of regular_invocable<int(), int> is a global thing that can be tested for anywhere, but the additional semantic requirement of regularity of "models" is tied to an invocation of an algorithm with a specific range, where regularity is required.

@frederick-vs-ja

Copy link
Copy Markdown
Contributor

My understanding is that e.g. it is well-formed to use std::sort on a std::vector<float> as long as there are no NaNs in that std::vector.

Perhaps we should say "well-defined" instead of "well-formed" as there shouldn't be dynamically determined well-formedness. In this case, semantic requirements for modeling should be transformed into preconditions of the function. But it's quite unclear how to transform them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants