On Fri, Jan 7, 2022 at 3:08 AM Михаил Найденов <mihailnajdenov@gmail.com> wrote:
On Thu, Jan 6, 2022 at 7:58 PM Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:
On Thu, Jan 6, 2022 at 11:13 AM Михаил Найденов via Std-Proposals <std-proposals@lists.isocpp.org> 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