Crimsontide
|
|
« on: September 11, 2018, 12:31:51 PM » |
|
Ok C++ gurus, I have the following 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
|
|
« 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
|
|
« 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
|
|
« 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
|
|
« 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 template<class T> struct B : private T { it should probably be template<class T> struct B : private A<T> {
|
|
|
Logged
|
|
|
|
Ordnas
|
|
« 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 template<class T> struct B : private T { it should probably be 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:
|
|
|
|
oahda
|
|
« 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
|
|
« 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
|
|
|
|
Schrompf
|
|
« 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
|
|
« 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
|
|
« 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? 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
|
|
« 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? 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 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
|
|
« 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? 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 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
|
|
« Reply #14 on: September 12, 2018, 05:06:04 AM » |
|
B<T> is probably meant to be used like this?
|
|
|
Logged
|
|
|
|
Crimsontide
|
|
« Reply #15 on: September 12, 2018, 05:45:45 AM » |
|
B<T> is probably meant to be used like this? Yup. Or in context where this is actually used: 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
|
|
« 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_rulesIf 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_typesThe 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
|
|
« 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_rulesIf 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_typesThe 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
|
|
« 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
|
|
« 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
|
|
|
|
|