• Why doesn't this build?

    From Vir Campestris@3:633/10 to All on Tue Dec 2 20:41:05 2025
    It's obviously extracted from a larger program, and I've done a bit of
    messing about to pin it down - and still can't see WTF is going on.

    Classes A and B both derive from std::pair. One of the classes to be
    stored in the pair is the templated class b, and a template parameter is supplied.

    In class A the template parameter is a fixed enum value.

    In class B it is a copy of the template parameter to class B, which is
    from the same enum.

    Yet A compiles while B doesn't. B doesn't think it has a pair as a base.
    Help!

    Andy

    ///////////////////////////////////////////////////
    #include <utility>

    enum e {E};

    template <e f> class b {};

    template <e f> class A : std::pair<int, b<E> >
    {
    A() : pair() {}
    };

    template <e f> class B : std::pair<int, b<f> >
    {
    B() : pair() {}
    };


    --
    I've spent far too long away playing with the project this comes from.
    Perhaps I should give it up. But it's decades since I wrote anything
    just for fun!

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Chris M. Thomasson@3:633/10 to All on Tue Dec 2 13:06:46 2025
    On 12/2/2025 12:41 PM, Vir Campestris wrote:
    enum e {E};

    template <e f> class b {};

    template <e f> class A : std::pair<int, b<E> >
    {
    ÿÿÿÿÿÿÿ A() : pair() {}
    };

    template <e f> class B : std::pair<int, b<f> >
    {
    ÿÿÿÿÿÿÿ B() : pair() {}
    };

    Time to get more explicit? Seems to work wrt:
    _________________________
    #include <utility>
    #include <iostream>

    enum e {E};

    template <e f> class b {};

    template <e f> class A : std::pair<int, b<E> >
    {
    A() : std::pair<int, b<E> >() {}
    };

    template <e f> class B : std::pair<int, b<f> >
    {
    B() : std::pair<int, b<f> >() {}
    };


    int main()
    {
    std::cout << "Hello..." << std::endl;

    return 0;
    }
    _________________________

    Does that help?

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Vir Campestris@3:633/10 to All on Tue Dec 2 21:15:06 2025
    On 02/12/2025 21:06, Chris M. Thomasson wrote:

    Time to get more explicit? Seems to work wrt:
    _________________________
    #include <utility>
    #include <iostream>

    enum e {E};

    template <e f> class b {};

    template <e f> class A : std::pair<int, b<E> >
    {
    ÿÿÿÿÿÿÿ A() : std::pair<int, b<E> >() {}
    };

    template <e f> class B : std::pair<int, b<f> >
    {
    ÿÿÿÿÿÿÿ B() : std::pair<int, b<f> >() {}
    };


    int main()
    {
    ÿÿÿ std::cout << "Hello..." << std::endl;

    ÿÿÿ return 0;
    }
    _________________________

    Does that help?

    Yes, It gets me out of my hole. But... Why do I need to be explicit in
    class B, but not in class A? The only difference is that the second pair parameter is variable not fixed.

    Thanks
    Andy

    --
    Do not listen to rumour, but, if you do, do not believe it.
    Ghandi.

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Chris M. Thomasson@3:633/10 to All on Tue Dec 2 13:43:25 2025
    On 12/2/2025 1:15 PM, Vir Campestris wrote:
    On 02/12/2025 21:06, Chris M. Thomasson wrote:

    Time to get more explicit? Seems to work wrt:
    _________________________
    [...]
    _________________________

    Does that help?

    Yes, It gets me out of my hole. But... Why do I need to be explicit in
    class B, but not in class A? The only difference is that the second pair parameter is variable not fixed.

    Not exactly sure. Humm. I noticed that wrt the following:
    _____________________
    #include <utility>
    #include <iostream>

    enum e {E};

    template <e f> class b {};

    template <typename f> class A : std::pair<int, b<E> >
    {
    A() : std::pair<int, b<E> >() {}
    };

    template <e f> class B : std::pair<int, b<f> >
    {
    B() : std::pair<int, b<f> >() {}
    };


    int main()
    {
    std::cout << "Hello..." << std::endl;

    return 0;
    }
    _____________________

    works (aka compiles), but if I put a typename for e in class B wrt <e
    , the compiler cakes its pants.

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Vir Campestris@3:633/10 to All on Wed Dec 3 11:05:14 2025
    On 02/12/2025 21:43, Chris M. Thomasson wrote:
    Not exactly sure. Humm. I noticed that wrt the following: _____________________
    #include <utility>
    #include <iostream>

    enum e {E};

    template <e f> class b {};

    template <typename f> class A : std::pair<int, b<E> >
    {
    ÿÿÿÿÿÿÿ A() : std::pair<int, b<E> >() {}
    };

    template <e f> class B : std::pair<int, b<f> >
    {
    ÿÿÿÿÿÿÿ B() : std::pair<int, b<f> >() {}
    };


    int main()
    {
    std::cout << "Hello..." << std::endl;

    ÿÿÿ return 0;
    }
    _____________________

    works (aka compiles), but if I put a typename for e in class B wrt <e
    , the compiler cakes its pants.

    It occurred to me later - what compiler are you using? I've tried G++
    and clang++ on Unix. I don't have MS compiler available.

    And I just realised I don't need that pair, just the templated base
    class b to show the problem.

    Andy

    --
    Do not listen to rumour, but, if you do, do not believe it.
    Ghandi.

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Wed Dec 3 04:10:52 2025
    On Tue 12/2/2025 12:41 PM, Vir Campestris wrote:

    ///////////////////////////////////////////////////
    #include <utility>

    enum e {E};

    template <e f> class b {};

    template <e f> class A : std::pair<int, b<E> >
    {
    ÿÿÿÿÿÿÿ A() : pair() {}
    };

    template <e f> class B : std::pair<int, b<f> >
    {
    ÿÿÿÿÿÿÿ B() : pair() {}
    };


    It is the same issue as in this example

    template <typename T> struct B { int x; };

    template <typename T> struct D : B<T> {
    D() {
    x = 5; // error: 'x' was not declared in this scope
    }
    };

    When compiling the subclass `D`, unqualified name lookup is not
    performed in base classes that depend on template parameters of `D`. For
    which reason unqualified name `x` (supposedly inherited from base
    `B<T>`) is not visible from members of `D`.

    In your case the same thing happens.

    The unqualified name `pair` you refer to in your code is an _injected_
    name inside base class `std::pair<>`, i.e. it is actually a member of `std::pair`. In order to find that `pair` the compiler has to perform unqualified lookup inside the base class `std::pair<>`.

    But in the second case the unqualified lookup inside is not performed
    for reasons I described above. Which is why unqualified name `pair` is
    not found.

    If you use a qualified name, the problem will go away, albeit in this
    case you will also have to reiterate the template arguments

    template <e f> class B : std::pair<int, b<f>>
    {
    B() : std::pair<int, b<f>>() {}
    };

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Chris M. Thomasson@3:633/10 to All on Wed Dec 3 16:06:38 2025
    On 12/3/2025 3:05 AM, Vir Campestris wrote:
    On 02/12/2025 21:43, Chris M. Thomasson wrote:
    Not exactly sure. Humm. I noticed that wrt the following:
    [...]
    It occurred to me later - what compiler are you using? I've tried G++
    and clang++ on Unix. I don't have MS compiler available.

    Oh, I used on online GCC compiler for it. Have not tried it on MSVC yet.


    And I just realised I don't need that pair, just the templated base
    class b to show the problem.

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Thu Dec 4 06:59:21 2025
    On Wed 12/3/2025 4:10 AM, Andrey Tarasevich wrote:

    It is the same issue as in this example

    ÿ template <typename T> struct B { int x; };

    ÿ template <typename T> struct D : B<T> {
    ÿÿÿ D() {
    ÿÿÿÿÿ x = 5; // error: 'x' was not declared in this scope
    ÿÿÿ }
    ÿ };

    When compiling the subclass `D`, unqualified name lookup is not
    performed in base classes that depend on template parameters of `D`. For which reason unqualified name `x` (supposedly inherited from base
    `B<T>`) is not visible from members of `D`.

    In your case the same thing happens.

    The unqualified name `pair` you refer to in your code is an _injected_
    name inside base class `std::pair<>`, i.e. it is actually a member of `std::pair`. In order to find that `pair` the compiler has to perform unqualified lookup inside the base class `std::pair<>`.

    But in the second case the unqualified lookup inside is not performed
    for reasons I described above. Which is why unqualified name `pair` is
    not found.

    If you use a qualified name, the problem will go away, albeit in this
    case you will also have to reiterate the template arguments

    ÿ template <e f> class B : std::pair<int, b<f>>
    ÿ {
    ÿÿÿ B() : std::pair<int, b<f>>() {}
    ÿ };


    ... when we deal with the `x` problem as in my small example, we can
    work around this issue either by using a qualified name

    D() {
    B::x = 5; // OK
    }

    or by referring to `x` through an explicit `this`

    D() {
    this->x = 5; // OK
    }

    In your case the workaround I provided above goes a different way: it
    refers to base class name directly (instead of referring to the injected name).

    But if you want, you can insist on referring to the injected name.
    Obviously, the `this->` version is not applicable here, but the
    qualified name approach will work

    template <e f> class B : std::pair<int, b<f> >
    {
    B() : std::pair<int, b<f>>::pair() {}
    };

    This is even longer, but in this version you are basically forcefully restoring your original intent.

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Vir Campestris@3:633/10 to All on Thu Dec 4 16:55:47 2025
    On 03/12/2025 12:10, Andrey Tarasevich wrote:
    <snip>
    If you use a qualified name, the problem will go away, albeit in this
    case you will also have to reiterate the template arguments

    ÿ template <e f> class B : std::pair<int, b<f>>
    ÿ {
    ÿÿÿ B() : std::pair<int, b<f>>() {}
    ÿ };

    "Problem" you say. Is this a compiler bug in both Gnu and Clang
    compilers, or is it supposed to be like it?

    Andy


    --
    Do not listen to rumour, but, if you do, do not believe it.
    Ghandi.

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Thu Dec 4 17:57:28 2025
    On Thu 12/4/2025 8:55 AM, Vir Campestris wrote:
    On 03/12/2025 12:10, Andrey Tarasevich wrote:
    <snip>
    If you use a qualified name, the problem will go away, albeit in this
    case you will also have to reiterate the template arguments

    ÿÿ template <e f> class B : std::pair<int, b<f>>
    ÿÿ {
    ÿÿÿÿ B() : std::pair<int, b<f>>() {}
    ÿÿ };

    "Problem" you say. Is this a compiler bug in both Gnu and Clang
    compilers, or is it supposed to be like it?


    No, it is not a bug. It is supposed to be like it. The rule that
    excludes depended base classes from unqualified name lookup in C++17 is

    17.6.2 Dependent names [temp.dep]
    3 In the definition of a class or class template, the scope of a
    dependent base class (17.6.2.1) is not examined during unqualified name
    lookup either at the point of definition of the class template or member
    or during an instantiation of the class template or member.

    (In later versions of the standard it's been relocated somewhere and
    reworded in some way apparently. I can't even find it.)

    This unqualified lookup peculiarity for template base classes is also described in quite a few FAQs. E.g.

    https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-types

    In your case it just happens to involve so called "injected class name",
    which is another fairly obscure C++ topic

    https://en.cppreference.com/w/cpp/language/injected-class-name.htm

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Andrey Tarasevich@3:633/10 to All on Thu Dec 4 22:00:42 2025
    On Thu 12/4/2025 5:57 PM, Andrey Tarasevich wrote:

    In your case it just happens to involve so called "injected class name", which is another fairly obscure C++ topic

    ÿ https://en.cppreference.com/w/cpp/language/injected-class-name.htm


    Lost a trailing 'l' in the link. Should be

    https://en.cppreference.com/w/cpp/language/injected-class-name.html

    --
    Best regards,
    Andrey

    --- PyGate Linux v1.5.1
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)