This code compiles with MSVC 2015, but doesn't compile with Clang 5.0.0 (trunk 304874):
template <typename T>
struct Base
{
T data;
};
template <typename T>
struct Derived : Base<T>
{
auto getData() const
{
return data;
}
};
Replacing data with this->data in Derived::getdata() makes Clang happy.
Which compiler is correct according to the C++ standard?
Must this-> be used in template code to access an identifier of a base class?
Clang is correct.
$17.6.2/3 Dependent names [temp.dep]
In the definition of a class or class template, the scope of a dependent base class 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.
For return data;, data is unqualified, then unqualified name lookup will be employed. This means the name in the base class Base<T> (which is a dependent base class because it depends on the template parameter T) shouldn't be found; i.e. the non-dependent names are not looked up in dependent base classes.
this->data or Base<T>::data makes it qualified. This means the name will be looked up at the time of instantiation, and at that time the exact base specialization that must be explored will be known.
Yes, it is. Basically data depends on T.
There is a mechanism called two-phase lookup. Non-(template)dependent names are resolved immediately - at the point of definition. Your data does not exist yet, because Base<T> does not exist yet, as it was not instantiated. Thus, it complains that data is not found.
You need to hint compiler that data depends on the template, and the name lookup should be performed in the second phase, after template parameters are substituted, i.e. template class was instantiated. This can be done by using this or providing template dependent scope.
So, either this->f() or Base<T>::f() will work.
Related
I am learning templates in C++. So reading examples from different sources(including SO). One such example whose statement i am unable to understand(or rather i think is incorrect) is given below:
class Base {
protected:
template <typename T1>
void print_pi() {
std::cout << (T1)3.141596 << std::endl;
};
};
template <typename T2>
class Derived : public Base {
};
template <typename T3>
class DoubleDerived : public Derived<T3> {
public:
void test() {
print_pi<int>(); // This creates error, however changing to Base::print_pi<int>(); works.
}
};
The user of the accepted answer says:
In the class Derived the name print_pi is a dependent name.
My question is that isn't print_pi a non-dependent name? I mean it doesn't doesn't depends on a template parameter. For instance, print_pi<T> is a dependent name but in OP's code inside the derived class DoubleDerived the name print_pi is non-dependent IMO. Can someone tell me why the user says that print_pi is a dependent name. I mean even print_pi<int> is non-dependent. I was thinking of writing an answer there saying that print_pi is a non-dependent name and according to Non-dependent name lookup members, the problem is that the compiler does not look in dependent base classes when looking up nondependent names but then some people closed the question.
Am i right in saying that the currently accepted answer is incorrect in saying that print_pi is a dependent name?
My question is that isn't print_pi a non-dependent name?
As written, print_pi<int> is a non-dependent name, so according to cppreference
For a non-dependent name used in a template definition, unqualified name lookup takes place when the template definition is examined.
We cannot look into base class (which is template), and no (template) function print_pi exist in global scope. So the compilation error.
Base::print_pi<int>(); is also not dependent. qualified name takes place:
If the lookup of the left hand side name comes up with a class/struct or union name, the name on the right hand side of :: is looked up in the scope of that class (and so may find a declaration of a member of that class or of its base), with the following exceptions [..]
Notice that it is not yet checked that Base is really a base of the class (Demo).
It would be done once the method would be instantiated (Demo)
this->template print_pi<int>();/Derived<T3>::template print_pi<int>(); are dependent. and so, from cppreference
For a dependent name used in a template definition, the lookup is postponed until the template arguments are known
So print_pi<int>() can be found in base classes (if present Demo (else error happens Demo)).
C++ allows to use class and function with the same name in one namespace:
struct S {};
void S() {}
In this case pure name S means function S. To use struct instead you need to explicitly add struct before name.
It's also possible to make function template and still use both of them:
template <class T>
void S() {}
struct S {};
But using template struct is forbidden
void S() {}
template <class T>
struct S {};
and gives error like this:
error: redefinition of 'S' as different kind of symbol
What is the reason for this? Why not to allow use template struct here? Is there any situation where using explicit keyword struct before S (like for non-template version) could not solve name collision if it was allowed? Maybe there is proposal exist?
C++ allows to use class and function with the same name in one namespace.
struct S {};
void S() {}
Normally when you declare struct S, you can refer to the type in two ways: as S and as struct S. But that's only until you declare something else named S, for example, a function. When you do that, the name of the type is not S any more. It's struct S only. The name S is reserved for the function.
This is done for compatibility with C. C code uses this device frequently. Unlike C++, C places struct and union tags in a different name space from normal identifiers, and struct S cannot be referred to as simply S.
So C++, in order to be able to compile C code that uses this device, makes an exception for struct tags that are reused as a different kind of identifier.
As class is nearly synonymous with struct, this is done for the class keyword too.
But C has no templates and there's no need to provide backward compatibility for them, so no such exception for class templates is made.
I think function template name is allowed to be the same as something else to allow overloading, while class template name is explicitly disallowed to match something else according to
14 Templates [temp]
A class template shall not have the same name as any other template, class, function, variable, enumeration, enumerator, namespace, or type in the same scope (3.3), except as specified in (14.5.5).
[update] the rest of this piece makes me think that the case with function template not causing the same error violates the standard requirements:
Except that a function template can be overloaded either by non-template functions (8.3.5) with the same name or by other function templates with the same name (14.8.3), a template name declared in namespace scope or in class
scope shall be unique in that scope.
and VS / clang / gcc seem to disagree on it godbolt
This code compiles with MSVC 2015, but doesn't compile with Clang 5.0.0 (trunk 304874):
template <typename T>
struct Base
{
T data;
};
template <typename T>
struct Derived : Base<T>
{
auto getData() const
{
return data;
}
};
Replacing data with this->data in Derived::getdata() makes Clang happy.
Which compiler is correct according to the C++ standard?
Must this-> be used in template code to access an identifier of a base class?
Clang is correct.
$17.6.2/3 Dependent names [temp.dep]
In the definition of a class or class template, the scope of a dependent base class 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.
For return data;, data is unqualified, then unqualified name lookup will be employed. This means the name in the base class Base<T> (which is a dependent base class because it depends on the template parameter T) shouldn't be found; i.e. the non-dependent names are not looked up in dependent base classes.
this->data or Base<T>::data makes it qualified. This means the name will be looked up at the time of instantiation, and at that time the exact base specialization that must be explored will be known.
Yes, it is. Basically data depends on T.
There is a mechanism called two-phase lookup. Non-(template)dependent names are resolved immediately - at the point of definition. Your data does not exist yet, because Base<T> does not exist yet, as it was not instantiated. Thus, it complains that data is not found.
You need to hint compiler that data depends on the template, and the name lookup should be performed in the second phase, after template parameters are substituted, i.e. template class was instantiated. This can be done by using this or providing template dependent scope.
So, either this->f() or Base<T>::f() will work.
I have the following code:
#include <iostream>
template <typename T>
struct Base
{
using Type = int;
};
template <typename T>
struct Derived : Base<T>
{
//uncommmenting the below cause compiler error
//using Alias = Type;
};
int main()
{
Derived<void>::Type b = 1;
std::cout << b << std::endl;
return 0;
}
Now the typename Type is available to Derived if its in a deduced context - as shown by the perfectly valid declaration of b. However, if I try to refer to Type inside the declaration of Derived itself, then I get a compiler error telling me that Type does not name a type (for example if the definition of Alias is uncommented).
I guess this is something to do with the compiler not being able to check whether or not Type can be pulled in from the base class when it is parsing the definition of Derived outside the context of a specific instantiation of parameter T. In this case, this is frustrating, as Base always defines Type irrespective of T. So my question is twofold:
1). Why on Earth does this happen? By this I mean why does the compiler bother parsing Derived at all outside of an instantiation context (I guess non-deduced context), when not doing so would avoid these 'bogus' compiler errors? Perhaps there is a good reason for this. What is the rule in the standard that states this must happen?
2). What is a good workaround for precisely this type of problem? I have a real-life case where I need to use base class types in the definition of a derived class, but am prevented from doing so by this problem. I guess I'm looking for some kind of 'hide behind non-deduced context' solution, where I prevent this compiler 'first-pass' by putting required definitions/typedefs behind templated classes or something along those lines.
EDIT: As some answers below point out, I can use using Alias = typename Base<T>::Type. I should have said from the outset, I'm aware this works. However, its not entirely satisfactory for two reasons: 1) It doesn't use the inheritance hierarchy at all (Derived would not have to be derived from Base for this to work), and I'm precisely trying to use types defined in my base class hierarchy and 2) The real-life case actually has several layers of inheritance. If I wanted to pull in something from several layers up this becomes really quite ugly (I either need to refer to a non-direct ancestor, or else repeat the using at every layer until I reach the one I need it at)
Because type is in a "dependent scope" you can access it like this:
typename Base<T>::Type
Your Alias should then be defined like this:
using Alias = typename Base<T>::Type;
Note that the compiler doesn't, at this point, know if Base<T>::type describes a member variable or a nested type, that is why the keyword typename is required.
Layers
You do not need to repeat the definition at every layer, here is an example, link:
template <typename T>
struct intermediate : Base<T>
{
// using Type = typename Base<T>::Type; // Not needed
};
template <typename T>
struct Derived : intermediate<T>
{
using Type = typename intermediate<T>::Type;
};
Update
You could also use the class it self, this relies on using an unknown specializations.
template <typename T>
struct Derived : Base<T>
{
using Type = typename Derived::Type; // <T> not required here.
};
The problem is that Base<T> is a dependent base class, and there may be specializations for it in which Type is not anymore defined. Say for example you have a specialization like
template<>
class Base<int>
{}; // there's no more Type here
The compiler cannot know this in advance (technically it cannot know until the instantiation of the template), especially if the specialization is defined in a different translation unit. So, the language designers chose to take the easy route: whenever you refer to something that's dependent, you need to explicitly specifify this, like in your case
using Alias = typename Base<T>::Type;
I guess this is something to do with the compiler not being able to check whether or not Type can be pulled in from the base class when it is parsing the definition of Derived outside the context of a specific instantiation of parameter T.
Yes.
In this case, this is frustrating, as Base always defines Type irrespective of T.
Yes.
But in general it would be entirely infeasible to detect whether this were true, and extremely confusing if the semantics of the language changed when it were true.
Perhaps there is a good reason for this.
C++ is a general-purpose programming language, not an optimised-for-the-program-Smeeheey-is-working-on-today programming language. :)
What is the rule in the standard that states this must happen?
It's this:
[C++14: 14.6.2/3]: In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope 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. [..]
What is a good workaround for precisely this type of problem?
You already know — qualification:
using Alias = typename Base<T>::Type;
When defining a template, sometimes things need to be a bit more explicit:
using Alias = typename Base<T>::Type;
Rougly speaking: a template is a blueprint of sorts. Nothing actually exists until the template get instantiated.
When doing the same thing, but in a non-template context, a C++ compiler will try to figure out what Type is. It'll try to find it in the base classes, and go from there. Because everything is already declared, and things are pretty much cut-and-dry.
Here, a base class does not really exist until the template gets instantiated. If you already know about template specialization, that you should realize that the base class may not actually turn out to have a Type member, when the template gets instantiated if there's a specialization for the base class defined later down the road, that's going to override the whole thing, and turn it inside and out.
As such, when encountering just a plain, old, Type in this context, the compiler can't make a lot of assumptions. It can't assume that it can look in any defined template base classes because those base classes may not actually look anything like the compiler thinks they will look, when things start to solidify; so you have spell everything out, explicitly, for the compiler, and tell the compiler exactly what your are trying to do, here.
You cannot use your base class types in a non-deduced context. C++ refuses to assume unbound names refer to things in your base class.
template <typename T>
struct Base {
using Type = int;
};
template<>
struct Base<int> {};
using Type=std::string;
template <typename T>
struct Derived : Base<T> {
using Alias = Type;
};
Now let us look at what is going on here. Type is visible in Derived -- a global one. Should Derived use that or not?
Under the rule of "parse nothing until instantiated", we use the global Type if and only if T is int, due to the Base specialization that removes Type from it.
Following that rule we run into the problem that we can diagnose basically no errors in the template prior to it being instantiated, because the base class could replace the meaning of almost anything! Call abs? Could be a member of the parent! Mention a type? Could be from the parent!
That would force templates to basically be macros; no meaningful parsing could be done prior to instantiating them. Minor typos could result in massively different behavior. Almost any incorrectness in a template would be impossible to diagnose without creating test instances.
Having templates that can be checked for correctness means that when you want to use your parent class types and members, you have to say you are doing so.
As a partial response about the point
[T]his is frustrating, as Base always defines Type irrespective of T.
I'd say: no it does not.
Please consider the following example differing from yours only by the one line definition of Base<void> and of the definition of Alias:
#include <iostream>
template <typename T>
struct Base
{
using Type = int;
};
template <typename T>
struct Derived : Base<T>
{
using Alias = typename Base<T>::Type; // error: no type named 'Type' in 'struct Base<void>'
};
template<> struct Base<void> {};
int main()
{
Derived<void>::Type b = 1;
std::cout << b << std::endl;
return 0;
}
In the context of template <typename T> struct Derived : Base<T>, there is no guaranty that Type exists. You must explicitly tell your compiler than Base<T>::Type is a type (with typename) and if you ever fail this contract, you'll end up with a compilation error.
Is there any scope problem in this program?
#include<iostream>
using namespace std;
template<class Type>
class Base
{
public:
Type member;
Base(Type param): member(param){
}
};
template<class Type>
class Derived: public Base<Type>
{
public:
Derived(Type param):Base<Type>(param){
// ^
// |_______ Not writing Type here gives error, why?
}
void display()
{
cout << member; /** ERROR HERE **/
}
};
int main()
{
Derived<int> p(5);
p.display();
return 0;
}
I get error 'member' was not declared in this scope. How to fix the problems?
Your question is somewhat confusing. At first I thought you were asking about base<Type> in the member initialization list, then I thought you were asking about accessing member, then back to the first... Now I'm thinking you're asking both, so I'll answer both.
Not writing Type here gives error, why?
When you use a class template's name (my_class_templ), it refers to the template, which is not a type. In order to use it as a type, you need to provide template parameters (my_class_templ<int>, my_class_templ<T>). So wherever a type name is needed (and that includes the base class names in an initialization list), you need to provide template parameters.
You can omit the template parameter list for class templates names within the class template's definition. For example, a copy constructor can be declared as
my_class_templ(const my_class_templ& rhs);
instead of
my_class_templ<T>(const my_class_templ<T>& rhs);
This is just a little syntactic sugar, allowing you to type less.
However, outside of the class templates definition, you need to explicitly spell out all the template parameters. This is also true for derived classes:
my_dervied_class_templ(const my_derived_class_templ& rhs)
: my_class_templ<T>(rhs) // need to spell out <T> here
{
}
I get error 'member' was not declared in this scope. How to fix the problems?
When your template is encountered first by the compiler, there's only its definition and no instantiations have been seen by the compiler yet. The compiler doesn't know whether, at a point of instantiation, there might be specializations of the template in scope or not. However, you could specialize your template for Base<T>::member to refer to something else or not to be defined entirely. (Say, a specialization Base<void> doesn't have a data member.) Therefore, the compiler must not speculate about the members of Base. Consequentially, they will not be looked up in Derived.
The result of this is that, if you need to refer to one of the members of Base, you need to tell the compiler that you expect a Base<T> to have such a member. This is done by fully qualifying its name: Base<Type>::member.
Not writing Type here gives error, why?
If you omit Type there is no way for the compiler to decide whether Base is a base class or is it a member of Derived. Specifying Type makes sure that Base is a template class [base class].
'member' was not declared in this scope
This is something to do with the rules for name lookup (dependent base classes).
C++03 [Section 14.6/8] says
When looking for the declaration of a name used in a template definition, the usual lookup rules (3.4.1, 3.4.2) are used for nondependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known (14.6.2).
Now Section 14.6.2/3 says
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope 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.
member is an unqualified name so the base class is not examined.
So you have two options.
Use fully qualified name of Member i.e Base<Type>::member
Use this->member.
At the point where the compiler reads the template (not when it instanciates it), it cannot tell what Base<Type> is (it could be specialized), and therefore doesn't attempt to deduce it has a member member. You have to explicitely tell it: cout << this->Base<Type>::member;.
I think (check it, I'm not sure) that a using Base<Type>::member at class scope works too.
The C++ standard requires a compiler to do a "two phase lookup" for templates. That is, they are trying to resolve all non-dependent names (names that don't depend on template parameters) in the first phase when the template is parsed and all the dependent names in the second phase when the template is instantiated.
If you don't qualify member it is treated as a non-dependent name and lookup fails in the first phase. You can solve this by prepending this-> to it. This makes member a dependent name and lookup is delayed until you actually instantiate the template.
Derived(Type param):Base<Type>(param){
That Base<Type> is required because the base of Derived is Base<T>. There is nothing called Base.
void display()
{
//cout << member; /** ERROR HERE **/
cout << this->member;
cout << this>Base<Type>::member;
}
Alternatively having a using declaration in the scope of 'Derived' is also a valid technique.