Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411476 Posts in 69369 Topics- by 58424 Members - Latest Member: FlyingFreeStudios

April 23, 2024, 02:37:19 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Unqualified name lookup
Pages: [1] 2
Print
Author Topic: Unqualified name lookup  (Read 1602 times)
Crimsontide
Level 5
*****


View Profile
« on: September 11, 2018, 12:31:51 PM »

Ok C++ gurus, I have the following code:

Code:
template<class T> struct A {
static constexpr T a = 5;
};

template<class T> struct B : private T {
void Print() { cout << a << endl; }
};

In visual C++ with the compiler option /permissive- this gives an error (identifier 'a' cannot be found), without /permissive- it compiles fine (and I can create an instance and happily print with it).  From my reading of https://en.cppreference.com/w/cpp/language/unqualified_lookup I don't think this is an error.  a in an unqualified name, and under 'Class Definition' part b) it says that: "the entire body of its base class(es), recursing into their bases when no declarations are found".  Am I correct, is this an error with visual C++, or am I misreading this?  Any thoughts?
Logged
ProgramGamer
Administrator
Level 10
******


aka Mireille


View Profile
« Reply #1 on: September 11, 2018, 08:13:24 PM »

If you’re trying to access a in A, then you would have to write A<T>::a. I’m not sure if that’s what you mean though.
Logged

Crimsontide
Level 5
*****


View Profile
« Reply #2 on: September 11, 2018, 08:36:44 PM »

That was the idea, access a in A.  But I'm pretty sure if I wanted to use a qualified name I'd use T::a not A<T>::a.

Not sure why I'd need to qualify the name though... an unqualified name should look in a struct/class base classes if I understand the link/spec properly.
Logged
ProgramGamer
Administrator
Level 10
******


aka Mireille


View Profile
« Reply #3 on: September 11, 2018, 08:59:10 PM »

Well, a is meaningless on its own since A is a template class, so you do need to explicitly access a through A<T> where T is the type of your choice.

Plus, unqualified scope seem to only talk about namespaces and not class scope.
Logged

buto
Level 0
***



View Profile WWW
« Reply #4 on: September 11, 2018, 11:20:30 PM »

I think the issue is that you are (accidentally!?) deriving from the wrong class.
Instead of
Code:
template<class T> struct B : private T {
it should probably be
Code:
template<class T> struct B : private A<T> {
Logged

Ordnas
Level 10
*****



View Profile WWW
« Reply #5 on: September 12, 2018, 12:17:54 AM »

I think the issue is that you are (accidentally!?) deriving from the wrong class.
Instead of
Code:
template<class T> struct B : private T {
it should probably be
Code:
template<class T> struct B : private A<T> {

Quote that, I am not very expert with templates, but that it is the first thing I thought about reading the code.
Logged

Games:

Crimsontide
Level 5
*****


View Profile
« Reply #6 on: September 12, 2018, 03:12:57 AM »

The deriving from T was intentional.  Its a commonly used technique for policy classes.  If you have a policy class (https://en.wikipedia.org/wiki/Policy-based_design) often they have no state, and so if inherited (instead of using normal composition), they take no size (https://en.cppreference.com/w/cpp/language/ebo).  But if they do have state, it works fine as well.  Deriving from A<T> would defeat the whole point of a policy class.
Logged
oahda
Level 10
*****



View Profile
« Reply #7 on: September 12, 2018, 03:34:27 AM »

I still think to you have to write T::a to show the compiler that it needs to get it from the templated type. I tried, and both GCC and Clang give the same error, and T::a fixes it.
Logged

Daid
Level 3
***



View Profile
« Reply #8 on: September 12, 2018, 03:36:29 AM »

I didn't try it, but doesn't give gcc a error along the lines of "cannot deduce a because it does not depend on any template parameters"?
Logged

Software engineer by trade. Game development by hobby.
The Tribute Of Legends Devlog Co-op zelda.
EmptyEpsilon Free Co-op multiplayer spaceship simulator
Schrompf
Level 9
****

C++ professional, game dev sparetime


View Profile WWW
« Reply #9 on: September 12, 2018, 03:45:58 AM »

This is simply wrong. Your B derives from T, but "a" is inside A<T>. A<T> and B<T> are not connected by any means, so naturally the compiler can't know that you want the "a" from A<T>.
Logged

Snake World, multiplayer worm eats stuff and grows DevLog
JWki
Level 4
****


View Profile
« Reply #10 on: September 12, 2018, 03:57:22 AM »

This is simply wrong. Your B derives from T, but "a" is inside A<T>. A<T> and B<T> are not connected by any means, so naturally the compiler can't know that you want the "a" from A<T>.

^This.
If you want to access A<T>::a for your T from which B is derived, you have to access it as A<T>::a.
Logged
buto
Level 0
***



View Profile WWW
« Reply #11 on: September 12, 2018, 04:29:04 AM »

I agree, there is no reason why a compiler should know that you mean 'a' in class A. Take the code below: which 'a' should the compiler choose?

Code:
template<class T> struct A {
static constexpr T a = 5;
};

template<class T> struct C {
static constexpr T a = 6;
};

template<class T> struct B : private T {
void Print() { cout << a << endl; }
};
Logged

Crimsontide
Level 5
*****


View Profile
« Reply #12 on: September 12, 2018, 04:50:44 AM »

I agree, there is no reason why a compiler should know that you mean 'a' in class A. Take the code below: which 'a' should the compiler choose?

Code:
template<class T> struct A {
static constexpr T a = 5;
};

template<class T> struct C {
static constexpr T a = 6;
};

template<class T> struct B : private T {
void Print() { cout << a << endl; }
};

Well if I was in charge of the standard Smiley I wouldn't issue an error until B was instantiated, then which 'a' would be perfectly clear.

But I appreciate the responses, and if GCC and Clang agree then clearly I am in the wrong.
Logged
Schrompf
Level 9
****

C++ professional, game dev sparetime


View Profile WWW
« Reply #13 on: September 12, 2018, 04:54:36 AM »

I agree, there is no reason why a compiler should know that you mean 'a' in class A. Take the code below: which 'a' should the compiler choose?

Code:
template<class T> struct A {
static constexpr T a = 5;
};

template<class T> struct C {
static constexpr T a = 6;
};

template<class T> struct B : private T {
void Print() { cout << a << endl; }
};

Well if I was in charge of the standard Smiley I wouldn't issue an error until B was instantiated, then which 'a' would be perfectly clear.

Ok, now I'm curious: which one would be the perfectly clear choice? Both are unrelated to B<T>.
Logged

Snake World, multiplayer worm eats stuff and grows DevLog
buto
Level 0
***



View Profile WWW
« Reply #14 on: September 12, 2018, 05:06:04 AM »

B<T> is probably meant to be used like this?
Code:
B<A<int>> b;
b.Print();
Logged

Crimsontide
Level 5
*****


View Profile
« Reply #15 on: September 12, 2018, 05:45:45 AM »

B<T> is probably meant to be used like this?
Code:
B<A<int>> b;
b.Print();


Yup.  Or in context where this is actually used:

Code:
template<class T> using HashSet = HashSetContainer<HashSetAdapter<T>>;
template<class T> using HashMultiSet = HashSetContainer<HashMultiSetAdapter<T>>;
template<class TK, class TD> using HashMap = HashSetContainer<HashMapAdapter<TK, TD>>;
template<class TK, class TD> using HashMultiMap = HashSetContainer<HashMultiMapAdapter<TK, TD>>;

Though I'm contemplating on renaming it to policy as opposed to adapter... because that's more accurate IMHO.
Logged
GenusNymphicus
Level 0
*



View Profile
« Reply #16 on: September 12, 2018, 07:09:12 AM »

To add to the other replies, the reason that this happens is because 'a' is not a dependent type. While the name is used inside the template, it doesn't directly depend on it. Adding T::, B<T>::  or even a this-> adds this kind of dependency.

The compiler does a two-phase lookup for templates. The first one happens before the instantiation, where the check of non-dependent names happens. The second one uses the "current instantiation" to check and resolve the depending names. However, since 'a' is non-dependent it will result in an error in the first phase.

From https://en.cppreference.com/w/cpp/language/dependent_name#Binding_rules
Quote
If the meaning of a non-dependent name changes between the definition context and the point of instantiation of a specialization of the template, the program is ill-formed,..

The list of dependent types: https://en.cppreference.com/w/cpp/language/dependent_name#Dependent_types
Quote
The following types are dependent types:
..
a member of an unknown specialization (see below)    
..

« Last Edit: September 12, 2018, 07:35:09 AM by GenusNymphicus » Logged
Crimsontide
Level 5
*****


View Profile
« Reply #17 on: September 12, 2018, 07:35:42 AM »

To add to the other replies, the reason that this happens is because 'a' is not a dependent type. While the name is used inside the template, it doesn't directly depend on it. Adding T::, B<T>::  or even a this-> adds this kind of dependency.

The compiler does a two-phase lookup for templates. The first one happens before the instantiation, where the check of non-dependent names happens. The second one uses the "current instantiation" to check and resolve the depending names. However, since 'a' is not-dependent it will result in an error in the first phase.

From https://en.cppreference.com/w/cpp/language/dependent_name#Binding_rules
Quote
If the meaning of a non-dependent name changes between the definition context and the point of instantiation of a specialization of the template, the program is ill-formed,..

The list of dependent types: https://en.cppreference.com/w/cpp/language/dependent_name#Dependent_types
Quote
The following types are dependent types:
..
a member of an unknown specialization (see below)    
..



That's a rather succinct way of describing it.  Well done.
Logged
JWki
Level 4
****


View Profile
« Reply #18 on: September 12, 2018, 11:07:03 AM »

For future reference if you had mentioned what buto figured out that you are instantiating B with A<T> as template type, you would have avoided the confusion in this thread - that should really have been part of the snippet you posted initially.
Without it, to me it seemed like you were somehow expecting B to have access to the field no matter what type you instantiate it with.
Logged
Crimsontide
Level 5
*****


View Profile
« Reply #19 on: September 12, 2018, 11:58:43 AM »

For future reference if you had mentioned what buto figured out that you are instantiating B with A<T> as template type, you would have avoided the confusion in this thread - that should really have been part of the snippet you posted initially.
Without it, to me it seemed like you were somehow expecting B to have access to the field no matter what type you instantiate it with.

I didn't add the instantiation simply because it wasn't required to generate the error.
Logged
Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic