Date: Fri, 7 Jan 2022 10:35:40 -0500
On Fri, Jan 7, 2022 at 3:08 AM Михаил Найденов <mihailnajdenov_at_[hidden]>
wrote:
> On Thu, Jan 6, 2022 at 7:58 PM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
> wrote:
>
>> On Thu, Jan 6, 2022 at 11:13 AM Михаил Найденов via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>>
>>> template<stuff_as_members T> //< constrained type!
>>> void func()
>>> {
>>> T::type val; //< typename should NOT be needed, type is known to
>>> be a typename
>>>
>>> T::templ<int>(); //< template should NOT be needed, templ is known to
>>> be a template
>>> }
>>>
>>
>>
>
>
>> I would guess not, because remember `typename` and `template` are hints
>> to the *parser* — nothing to do with semantic analysis. So the parser
>> knows that the token sequence
>> template < stuff_as_members T >
>> is introducing a constrained template, but it's not going to go analyze
>> `concept stuff_as_members` to find out what its requirements *are*.
>>
>
> That looks like an area of improvement to me - if we can use the concept
> beyond syntactical checks to improve the code, we should do so.
> For example
>
> template<stuff_as_members T>
> void func()
> {
> T::type += 1;
> }
>
> Could be caught right in definition time, rather than instantiation time,
> catching the error as early as possible.
> [...]
> Can the look-up really change (in an undesired way), considering the
> compiler will simply have to "insert the keywords", based on the names,
> found in the concept?
>
This sounds like the "fire" problem described in (among other papers)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0782r2.html
We might know that "Type A models concept C" requires `fire(a)` to be
provided; and we might know that "Type B is a dependent type that models
concept C," but that doesn't necessarily mean that when we say `fire(b)` we
necessarily make a call to the `fire` associated with concept C (because
there might be a better-matching overload provided by B outside of the
concept).
The point of my previous message was that we might know that "Type A models
concept C" requires `A::type` to be a type; and we might know that "Type B
is a dependent type that models concept C," but that doesn't necessarily
mean that when we say `B::type` we necessarily get a type (because there
*might* be a better-looked-up meaning for that name provided by B outside
of the concept — or at least, we should assume there might be, until you
can *prove* that's impossible, i.e. that all the places we see it happening
in practice today are due to compiler bugs).
Basically, anything that messes with name lookup is a bad idea, and needs
>> motivation stronger than "hey we could do this." The motivation should be,
>> like, "We *should* do this *because*..." (and then insert something
>> really compelling).
>>
>
> Because the code should not literally break if one changes a concrete type
> to a parameterized type, as it does today, aiming for Bjarne's "Generic
> programming is just programming" overarching goal.
>
Ah, you want to be able to take
struct Foo { Bar x(); };
*Bar* test(*Foo* f) { return f.x(); }
and blindly change it to
template<class T> concept Fooable = true;
template<class T> concept Barable = true;
*Barable auto* test(*Fooable auto* f) { return f.x(); }
and have that transformation Just Work. Unfortunately, at the moment a
bigger blocker for that is that you can't put constrained-auto inside
containers, e.g.
void test2(Foo f) {
std::vector<*Bar*> v; // OK
v.push_back(f.x());
}
void test2(*Fooable auto* f) {
std::vector<*Barable auto*> v; // Error
v.push_back(f.x());
}
And of course that you have to move that now-a-template function out of its
.cpp file into a .h file... but perhaps that's minimized by either the rise
of header-only libraries, or C++20 Modules. (You can minimize a lot of
problems by saying "Modules will surely solve that!" ;))
But basically, function templates are not functions (they're *templates*
for stamping out functions), and so it shouldn't be too surprising that
they work a little differently.
–Arthur
>
wrote:
> On Thu, Jan 6, 2022 at 7:58 PM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
> wrote:
>
>> On Thu, Jan 6, 2022 at 11:13 AM Михаил Найденов via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>>
>>> template<stuff_as_members T> //< constrained type!
>>> void func()
>>> {
>>> T::type val; //< typename should NOT be needed, type is known to
>>> be a typename
>>>
>>> T::templ<int>(); //< template should NOT be needed, templ is known to
>>> be a template
>>> }
>>>
>>
>>
>
>
>> I would guess not, because remember `typename` and `template` are hints
>> to the *parser* — nothing to do with semantic analysis. So the parser
>> knows that the token sequence
>> template < stuff_as_members T >
>> is introducing a constrained template, but it's not going to go analyze
>> `concept stuff_as_members` to find out what its requirements *are*.
>>
>
> That looks like an area of improvement to me - if we can use the concept
> beyond syntactical checks to improve the code, we should do so.
> For example
>
> template<stuff_as_members T>
> void func()
> {
> T::type += 1;
> }
>
> Could be caught right in definition time, rather than instantiation time,
> catching the error as early as possible.
> [...]
> Can the look-up really change (in an undesired way), considering the
> compiler will simply have to "insert the keywords", based on the names,
> found in the concept?
>
This sounds like the "fire" problem described in (among other papers)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0782r2.html
We might know that "Type A models concept C" requires `fire(a)` to be
provided; and we might know that "Type B is a dependent type that models
concept C," but that doesn't necessarily mean that when we say `fire(b)` we
necessarily make a call to the `fire` associated with concept C (because
there might be a better-matching overload provided by B outside of the
concept).
The point of my previous message was that we might know that "Type A models
concept C" requires `A::type` to be a type; and we might know that "Type B
is a dependent type that models concept C," but that doesn't necessarily
mean that when we say `B::type` we necessarily get a type (because there
*might* be a better-looked-up meaning for that name provided by B outside
of the concept — or at least, we should assume there might be, until you
can *prove* that's impossible, i.e. that all the places we see it happening
in practice today are due to compiler bugs).
Basically, anything that messes with name lookup is a bad idea, and needs
>> motivation stronger than "hey we could do this." The motivation should be,
>> like, "We *should* do this *because*..." (and then insert something
>> really compelling).
>>
>
> Because the code should not literally break if one changes a concrete type
> to a parameterized type, as it does today, aiming for Bjarne's "Generic
> programming is just programming" overarching goal.
>
Ah, you want to be able to take
struct Foo { Bar x(); };
*Bar* test(*Foo* f) { return f.x(); }
and blindly change it to
template<class T> concept Fooable = true;
template<class T> concept Barable = true;
*Barable auto* test(*Fooable auto* f) { return f.x(); }
and have that transformation Just Work. Unfortunately, at the moment a
bigger blocker for that is that you can't put constrained-auto inside
containers, e.g.
void test2(Foo f) {
std::vector<*Bar*> v; // OK
v.push_back(f.x());
}
void test2(*Fooable auto* f) {
std::vector<*Barable auto*> v; // Error
v.push_back(f.x());
}
And of course that you have to move that now-a-template function out of its
.cpp file into a .h file... but perhaps that's minimized by either the rise
of header-only libraries, or C++20 Modules. (You can minimize a lot of
problems by saying "Modules will surely solve that!" ;))
But basically, function templates are not functions (they're *templates*
for stamping out functions), and so it shouldn't be too surprising that
they work a little differently.
–Arthur
>
Received on 2022-01-07 09:35:53