C++ Logo


Advanced search

Re: No need for typename and template once it's stated in the concept

From: Михаил Найденов <mihailnajdenov_at_[hidden]>
Date: Sun, 9 Jan 2022 14:34:21 +0200
On Fri, Jan 7, 2022 at 5:35 PM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>

> 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).
Interesting, my understanding is however, that it is impossible for that
name to not be a type and still have a correct code. This is, while in the
'fire' example both the code and the concept can differ, while both
being also correct, here it is impossible. If the name in the body is not a
type (and the concept says it must be), the function is broken, even if
"the body is correct for a certain point of view".

> 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-09 12:34:34