Date: Mon, 2 Sep 2024 14:55:52 -0400
On Mon, Sep 2, 2024 at 2:35 PM Robin Savonen Söderholm via
Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Hi!
>
> I've come into situations with concepts where I'd like to do something along the lines of this:
> ```c++
>
> template <typename T>
> concept has_foo = requires(T&& t, int i /* and so on...*/ ) { t.foo1(i); /*and maybe more functions*/};
>
> template <typename T>
> concept fooable = requires(T&& t, has_foo auto&& f) { t.do_foo_stuff(f); };
>
> ```
> , but I can't as far as I know. So as far as I can tell, I have 2 options here: add a second template parameter to "fooable" or create a dummy class that fullfills "has_foo". However, none of these options are always optimal. If we consider the following example instead:
> ```c++
> template <typename T>
> concept has_foo = requires(T&& t, int i, float f) { t.foo(i); t.foo_bar(i, f); };
>
> template <typename T>
> concept fooable = requires(T&& t, has_foo auto&& f) { t.do_foo_stuff(f); };
>
> template <fooable TF>
> class handler_for_fooable {
> TF f_;
> public:
> void bar(auto&& f) requires(requires {f_.do_foo_stuff(f); }) {
> // do stuff.
> f_.do_foo_stuff(f);
> }
> };
> ```
> In this case, we can discuss the varying problems with each solution.
> If we add another template parameter to "fooable", we must know at instantiation of handler_for_fooable what type will be used in bar(). Furthermore, we must either require that the second parameter to "has_foo" is a "fooable" which may be too restrictive or, we skip the "has_foo" requirement and risk having to look at a very large wall of text to figure out if the "has_foo"-type or the "fooable" is wrong. If we instead add dummy parameter that fullfills "has_foo", we remove the earlier problems mentioned, but instead get a few new ones: first we have to do double bookkeeping on the interfaces the concepts describe, secondary the API description for a user of the library will look fishy and someone may accidentially use the dummy type in their function signature or what not, and thirdly the dummy type may be incorrect (what about copy/move construction/assignment for example?)
>
> I would like to have a way to express "given some type that fulfills the constraint X, type T shall fulfil Y."
> I understand that this can be a very expensive feature, but I would like to at least discuss the idea. Or is there a paper for something similar?
>
> // Robin
The problem is that constraints are not applied on the basis of
hypothetical types. They're applied on the basis of *actual* types.
The concept `fooable` fundamentally represents a relationship between
two unknown types. Not "one concrete type and some unknown family of
types fulfilling some constraint", but "two concrete types".
When you provide template parameters to a constraint, the system has
to check, at that point, whether the given parameters fulfill the
requirements of that constraint. So it has to substitute in actual
types and check that the expressions are valid and result in what the
constraints say they should result in.
So, how would `t.do_foo_stuff(f)` work? The type of `f` is not known.
So how could the system tell if `t.do_foo_stuff(f)` would be a legal
expression? How would it do overload resolution if it doesn't have a
concrete type to use that overload resolution on? How would template
argument substitution work in that function call?
I get the idea you're trying to do, but it simply isn't reasonable.
Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Hi!
>
> I've come into situations with concepts where I'd like to do something along the lines of this:
> ```c++
>
> template <typename T>
> concept has_foo = requires(T&& t, int i /* and so on...*/ ) { t.foo1(i); /*and maybe more functions*/};
>
> template <typename T>
> concept fooable = requires(T&& t, has_foo auto&& f) { t.do_foo_stuff(f); };
>
> ```
> , but I can't as far as I know. So as far as I can tell, I have 2 options here: add a second template parameter to "fooable" or create a dummy class that fullfills "has_foo". However, none of these options are always optimal. If we consider the following example instead:
> ```c++
> template <typename T>
> concept has_foo = requires(T&& t, int i, float f) { t.foo(i); t.foo_bar(i, f); };
>
> template <typename T>
> concept fooable = requires(T&& t, has_foo auto&& f) { t.do_foo_stuff(f); };
>
> template <fooable TF>
> class handler_for_fooable {
> TF f_;
> public:
> void bar(auto&& f) requires(requires {f_.do_foo_stuff(f); }) {
> // do stuff.
> f_.do_foo_stuff(f);
> }
> };
> ```
> In this case, we can discuss the varying problems with each solution.
> If we add another template parameter to "fooable", we must know at instantiation of handler_for_fooable what type will be used in bar(). Furthermore, we must either require that the second parameter to "has_foo" is a "fooable" which may be too restrictive or, we skip the "has_foo" requirement and risk having to look at a very large wall of text to figure out if the "has_foo"-type or the "fooable" is wrong. If we instead add dummy parameter that fullfills "has_foo", we remove the earlier problems mentioned, but instead get a few new ones: first we have to do double bookkeeping on the interfaces the concepts describe, secondary the API description for a user of the library will look fishy and someone may accidentially use the dummy type in their function signature or what not, and thirdly the dummy type may be incorrect (what about copy/move construction/assignment for example?)
>
> I would like to have a way to express "given some type that fulfills the constraint X, type T shall fulfil Y."
> I understand that this can be a very expensive feature, but I would like to at least discuss the idea. Or is there a paper for something similar?
>
> // Robin
The problem is that constraints are not applied on the basis of
hypothetical types. They're applied on the basis of *actual* types.
The concept `fooable` fundamentally represents a relationship between
two unknown types. Not "one concrete type and some unknown family of
types fulfilling some constraint", but "two concrete types".
When you provide template parameters to a constraint, the system has
to check, at that point, whether the given parameters fulfill the
requirements of that constraint. So it has to substitute in actual
types and check that the expressions are valid and result in what the
constraints say they should result in.
So, how would `t.do_foo_stuff(f)` work? The type of `f` is not known.
So how could the system tell if `t.do_foo_stuff(f)` would be a legal
expression? How would it do overload resolution if it doesn't have a
concrete type to use that overload resolution on? How would template
argument substitution work in that function call?
I get the idea you're trying to do, but it simply isn't reasonable.
Received on 2024-09-02 18:56:04