enum e {E};
template <e f> class b {};
template <e f> class A : std::pair<int, b<E> >
{
-a-a-a-a-a-a-a A() : pair() {}
};
template <e f> class B : std::pair<int, b<f> >
{
-a-a-a-a-a-a-a 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-a-a-a-a-a-a A() : std::pair<int, b<E> >() {}
};
template <e f> class B : std::pair<int, b<f> >
{
-a-a-a-a-a-a-a B() : std::pair<int, b<f> >() {}
};
int main()
{
-a-a-a std::cout << "Hello..." << std::endl;
-a-a-a return 0;
}
_________________________
Does that help?
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.
, the compiler cakes its pants.--- Synchronet 3.21a-Linux NewsLink 1.2
///////////////////////////////////////////////////
#include <utility>
enum e {E};
template <e f> class b {};
template <e f> class A : std::pair<int, b<E> >
{
-a-a-a-a-a-a-a A() : pair() {}
};
template <e f> class B : std::pair<int, b<f> >
{
-a-a-a-a-a-a-a B() : pair() {}
};
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.
And I just realised I don't need that pair, just the templated base--- Synchronet 3.21a-Linux NewsLink 1.2
class b to show the problem.
It is the same issue as in this example
-a template <typename T> struct B { int x; };
-a template <typename T> struct D : B<T> {
-a-a-a D() {
-a-a-a-a-a x = 5; // error: 'x' was not declared in this scope
-a-a-a }
-a };
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
-a template <e f> class B : std::pair<int, b<f>>
-a {
-a-a-a B() : std::pair<int, b<f>>() {}
-a };
If you use a qualified name, the problem will go away, albeit in this
case you will also have to reiterate the template arguments
-a template <e f> class B : std::pair<int, b<f>>
-a {
-a-a-a B() : std::pair<int, b<f>>() {}
-a };
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
-a-a template <e f> class B : std::pair<int, b<f>>
-a-a {
-a-a-a-a B() : std::pair<int, b<f>>() {}
-a-a };
"Problem" you say. Is this a compiler bug in both Gnu and Clang
compilers, or is it supposed to be like it?
In your case it just happens to involve so called "injected class name", which is another fairly obscure C++ topic
-a https://en.cppreference.com/w/cpp/language/injected-class-name.htm
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
-a 17.6.2 Dependent names [temp.dep]
-a 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.html
The isocpp.org website just says
"An Error Was Encountered
Site Error: Unable to Load Site Preferences; No Preferences Found"
I tried both Chrome and Firefox.
I found that I could drop pair from my examples, and I have this:
enum e {Z};
template <e f> class b {};
template <e f> class C : b<Z>
{
-a-a-a-a-a-a-a // dependent name lookup finds b OK here
-a-a-a-a-a-a-a // and knows it means the base class b<Z>
-a-a-a-a-a-a-a C() : b() {}
};
template <e f> class D : b<f>
{
-a-a-a-a-a-a-a // In this case b<f> is not found, even
-a-a-a-a-a-a-a // though it is the only base class
-a-a-a-a-a-a-a D() : b() {}
};
It's not like the cases where a class has two bases with different
template parameters. There is a single base class and no possible
ambiguity.
That is exactly the same problem.
Again, when you write `b` is the constructor initializer lists of
classes `C` and `D`, you are actually referring to the injected-class-
name `b`, which is invisibly present inside the `b<>` template class.
Think of it this way: every time you define
-a template <e f> class b {
-a-a-a ...
the compiler quietly inserts an implicit/invisible typename declaration
as the very first line of your class template
-a template <e f> class b {
-a-a-a using b = /* the current specialization of `b<>` */;
-a-a-a ...
The existence of that nested `b` is the reason you can refer to the
current specialization of template class `b<>` as a mere `b` (i.e.
without explicitly specifying its template arguments). Jut like you previously referred to `std::pair<whatever>` as a mere `pair`.
This `b`, implicitly introduced by the compiler, is the so called injected-class-name. It exists in all classes, template or non-template.
And when you mention `b` in the constructor initializer lists of `C` and `D`, the unqualified name lookup looks for `b`. There's nothing else it
can find besides the injected-class-name inside the `b<>` template. That injected-class-name `b` is the only chance for unqualified name lookup
to succeed. And if for some reason that lookup fails, your code will
fail to compile.
For your `C` the lookup succeeds, since the base class `b<Z>` is not a dependent type. It is included into the lookup, so the compiler
successfully finds `b<Z>::b`.
But for your `D` the base class `b<f>` is a dependent type. It is not included into the lookup (for reasons I described previously). So,
`b<f>::b` is not found. Lookup for `b` fails.
OK, I think I have it. I don't like it, but I understand.
https://en.cppreference.com/w/cpp/language/dependent_name.html
includes inter alia
"a type used in a non-dependent name is incomplete at the point of definition but complete at the point of instantiation"
as one of the reasons why it won't work.
On Mon 12/8/2025 9:33 AM, Vir Campestris wrote:
OK, I think I have it. I don't like it, but I understand.
https://en.cppreference.com/w/cpp/language/dependent_name.html
includes inter alia
"a type used in a non-dependent name is incomplete at the point of
definition but complete at the point of instantiation"
as one of the reasons why it won't work.
I'm not sure what kind of "reasons" you are referring to.
The immediate reason (i.e. "because the standard says so") is the rule I have already mentioned, linked and illustrated previously: dependent
base classes are not inspected when performing into unqualified name
lookup. (I see that isocpp.org is back online.)
As for rationale-kind of reason (i.e. "why does standard say so?"), it
is quite different. It is related to the fact in many template contexts
the compiler need to know whether a given dependent name is a type name,
a template name or a function/variable name. This is why we sometime
have to explicitly spell-out such keywords as `typename` or `template`
in template contexts to disambiguate the nature of some dependent names.
A system for this kind of disambiguation exists in C++ for _qualified_ names, but there's no such system for _unqualified_ names. Instead of expanding this system to cover unqualified names, the language simply decided to take the easy way out: postulate that names inherited from dependent base classes are "invisible" to unqualified name lookup. If
you want to access such names, refer to them through their _qualified_ names, thus engaging _qualified_ lookup and all the rules it brings with
it.
| Sysop: | Amessyroom |
|---|---|
| Location: | Fayetteville, NC |
| Users: | 54 |
| Nodes: | 6 (1 / 5) |
| Uptime: | 22:45:53 |
| Calls: | 742 |
| Files: | 1,218 |
| D/L today: |
6 files (8,794K bytes) |
| Messages: | 186,546 |
| Posted today: | 1 |