Finding names in templatized base classes in C++ - c++

I'm reading Effective C++ 3rd Edition, item 43 "Know how to access names in templatized base classes".
template<typename T>
class B {
T i;
};
template<typename T>
class D: public B<T> {
public:
void Foo() {
T a = B<T>::i;
}
};
int main() {
D<int> d;
}
For the above codes, I know if the B<T>:: is not added before i in D::Foo(), compilers will complain "i was not declared in this scope". (But it didn't complain i is private in B.)
However, if T i; is not declared in B, like the following, the compiling goes well.
template<typename T>
class B {
};
template<typename T>
class D: public B<T> {
public:
void Foo() {
T a = B<T>::i;
}
};
int main() {
D<int> d;
}
Compilers are defaulted not to find names in templatized base classes.
But why they still don't do even I told them?

But why they still don't do even I told them?
Because the member function Foo is not used, then it's not instantiated at all.
This applies to the members of the class template: unless the member is used in the program, it is not instantiated, and does not require a definition.
You might get an error if Foo is called, like
D<int> d;
d.Foo();
BTW
But it didn't complain i is private in B.
Because accessibility check is performed after name lookup. The name i is not found, then accessibility of nothing could be checked.

Related

Why are methods and attributes of base template classes not immediately available? [duplicate]

Why is it that the C++ standard specify that unqualified names in a template are non-dependent?
e.g.
template<typename T>
class Base
{
public:
T x;
};
template<typename T>
class C : public Base<T>
{
public:
bool m() { return x == 0; } // Error: undeclared identifier 'x'
};
Quoting from the accepted answer to an SO question about how to overcome the restriction:
The standard specifies that unqualified names in a template are
non-dependent and must be looked up when the template is defined. The
definition of a dependent base class is unknown at that time
(specializations of the base class template may exist) so unqualified
names are unable to be resolved.
However, the quoted and other answers do not specify why this is what the standard specifies. What is the rationale for this restriction?
This isn't quite what the standard actually says. What it actually says is that dependent base classes are not examined during unqualified name lookup. An unqualified name can of course be dependent given the correct context - that's how ADL in templates works: given a template type parameter T, the foo in foo(T()) is a dependent name.
In any event, the reason it can't do this kind of lookup is straightforward: at template definition time, you have no idea what the dependent base class is going to look like, thanks to specializations that may come in later, so you can't do any meaningful lookup. Every misspelled identifier in a template with a dependent base class can't be diagnosed until when it is instantiated, if ever. And since every unqualified identifier might have been from a dependent base class, you'll need some way to answer "is this a type?" and "is this a template?", since the answers to those questions affect parsing. That means something like the dreaded typename and template keywords, but in far more places.
I think it is a matter of consistency. Consider this, a bit modified example:
header file:
template<typename T>
class Base
{
public:
T x;
};
extern int x;
template<typename T>
class C : public Base<T>
{
public:
bool m() { return x == 0; }
};
and source file:
template<>
class Base<int> // but could be anything
{
public:
// no x here
};
int main()
{
C<char> c1;
c1.m(); // may select ::x or Base<char>::x
C<int> c2;
c2.m(); // has only one choice: ::x
return(0);
}
Wording in the standard guarantees that compiler will either error out or select whatever symbol it sees at the point of template definition. If the compiler was to defer name resolution to template instantiation it may then select different objects visible at this point and which may surprise the developer.
If the developer WANTS to access dependent name he is required to state this explicitly and he shouldn't be then taken off guard.
Please also note that if the x wasn't available at the point of template definition the following code would break (unexpectedly) One Definition Rule:
one.cpp
#include <template_header>
namespace {
int x;
}
void f1()
{
C<int> c1;
}
two.cpp
#include <template_header>
namespace {
char x;
}
void f2()
{
C<int> c2;
}
One would expect that c1 and c2 variables are of the same type and can for example be safely passed to a function taking C<int> const & parameter, but basically the two variables have the same type name but the implementations differ.
Whether compliant with the standard or not, in Visual Studio 2015 - this isn't an issue at all. Moreover, despite the comment from Tomek, the compiler takes the correct x
static int x = 42;
template<typename T>
class Base {
public:
T x;
Base() { x = 43; }
};
template<>
class Base<int> { };
template<typename T>
class C :public Base<T>
{
public:
int m() { return x; }
};
void main() {
C<int> cint;
C<char> cchar;
std::cout << "cint: " << cint.m() << std::endl;
std::cout << "cchar: " << cchar.m() << std::endl;
}
// Output is:
// cint: 42
// cchar: 43

CRTP and visibility of a type that is defined by the base class

Here a brief example of a code that works. It helps to introduce the actual question.
The specifiers for the visibility are the same used in the real code.
class Base {
public:
using foo = int;
virtual ~Base() { }
protected:
static foo bar() noexcept {
static foo v = 0;
return v++;
}
};
template<class Derived>
class Class: public Base {
static foo get() noexcept {
static foo v = bar();
return v;
}
};
int main() { }
It follows the previous example, even though slightly modified.
A template parameter has been added to the base class and the derived one has been updated accordingly.
This one doesn't compile.
template<typename T>
class Base {
public:
using foo = int;
virtual ~Base() { }
protected:
static foo bar() noexcept {
static foo v = 0;
return v++;
}
};
template<class Derived, typename T>
class Class: public Base<T> {
static foo get() noexcept {
static foo v = bar();
return v;
}
};
int main() { }
The error is quite clear indeed and the problem is not to solve it:
main.cpp:18:12: error: ‘foo’ does not name a type
static foo get() noexcept {
^
main.cpp:18:12: note: (perhaps ‘typename BaseComponent<T>::foo’ was intended)
The actual question is: why foo is not visible without a scope specifier in the second example?
I mean, Class is derived from Base<T>, that is (at least in my mind) a fully defined type, thus foo should be part of the base class and thus visible to the derived one, as it happens in the first example.
It follows an example that, according to what I guess of the problem, actually compiles and should not, or at least should behave like the previous one:
template <typename T>
struct B {
using foo = T;
};
struct D: public B<int> {
static foo bar;
};
int main() {
B<int> *b = new D;
}
Could it be due to the fact that the derived class is not a templated one in this case?
Honestly, it seems to me a bit strange, because the foo type is part of the base class that is still a templated one, so it shouldn't be much different from the previous one.
It goes without saying that I'm wrong, but I cannot figure out what's wrong in my thoughts.
Thank you in advance for the help.
That's because of name lookup rules. If your base class is a template, the unqualified name in base is not resolved. The reason is that later in your code you may have a template specialization of that base which doesn't define the name, or for which the name means something completely different. It's too complicated for the compiler to figure out whether you have a specialization, and if so, whether your name means the same thing in it. So it prefers to defer the name lookup. To make the compiler "believe you", then you need to either use a qualified name, or this->name instead (for members, not for typedefs), and the compiler will resolve the name.

How to call a method from base class where the base class is a template parameter

i have a c++ class like the below one working at vc++ , but doesnt work anymore in linux gcc 4.7. And i have no idea how to make it work again.
test.h
template<typename a>
class test: public a
{
public:
void fun();
};
test.cpp
template<typename a>
void test<a>::fun()
{
template_class_method(); <-- this is a public method from template_class
}
template class test<template_class>;
template_class.h
class template_class {
public:
template_class();
virtual ~template_class();
void template_class_method();
};
template_class.cpp
#include "templateclass.h"
template_class::template_class() {
// TODO Auto-generated constructor stub
}
template_class::~template_class() {
// TODO Auto-generated destructor stub
}
void template_class::template_class_method() {
}
You need to qualify it with the base class name as:
a::template_class_method();
The qualification a:: is necessary because template_class_method exists in a. The C++ rule is that if the base is a class template or template argument, then all its members are not automatically visible to the derived classes. In order to help the compiler to find the member, you need to tell it to look for the member in the base class, for which you need to qualify the member which is of the form of base::member_function() or base::member_data.
In your case, since the base is a, and the member is template_class_method, so you have to write this:
a::template_class_method();
Note that such a base class is called dependent base class since it depends on the template argument.
I don't know why #Karthik T deleted the answer, but that answer was on the right path. You have several options
Use qualified name a::template_class_method()
template<typename a>
class test : public a
{
public:
void fun()
{
a::template_class_method();
}
};
Use class member access syntax this->template_class_method()
template<typename a>
class test : public a
{
public:
void fun()
{
this->template_class_method();
}
};
Make the base class method visible through using-declaration
template<typename a>
class test : public a
{
public:
using a::template_class_method;
void fun()
{
template_class_method();
}
};
Note that the first method will suppress the virtuality of template_class_method (in cases when it is virtual), so it should be used with caution. For this reason, method number 2 is preferred, since it preserves the natural behavior of template_class_method.
Your comment stating that this->template_class_method() "doesn't work" is unclear. It works without any problems. Moreover, as I said above, this is in general case a better option than using a qualified name.

Refer base class members from derived class

class A {
public:
void fa() {
}
};
class B : public A{
public:
void fb() {
}
};
class C : public A, public B {
public:
void fc() {
//call A::fa(), not B::A::fa();
}
};
How to call A::fa() from C::fc() function.
GCC warns withdirect base A inaccessible in C due to ambiguity, does this mean there is no direct way to refer base class members?
One option would be to create a stub class that you can use for casting to the right base class subobject:
struct A {
void fa() { }
};
struct B : A {
void fb() { }
};
// Use a stub class that we can cast through:
struct A_ : A { };
struct C : A_, B {
void fc() {
implicit_cast<A_&>(*this).fa();
}
};
Where implicit_cast is defined as:
template <typename T> struct identity { typedef T type; }
template <typename T>
T implicit_cast(typename identity<T>::type& x) { return x; }
I just found the following info from ISO C++ 2003 standard (10.1.3)
A class shall not be specified as a direct base class of a derived class more than once. [Note: a class can be
an indirect base class more than once and can be a direct and an indirect base class. There are limited
things that can be done with such a class. The non-static data members and member functions of the direct
base class cannot be referred to in the scope of the derived class. However, the static members, enumerations
and types can be unambiguously referred to.
It means there is no direct way :(
I just compiled you code on codepad.org , putting A::fa() is enough to call fa() from your C::fc() function.
void fc() {
A::fa();
}
The below is the link to codepad with your code.
http://codepad.org/NMFTFRnt
I don't think you can do what you want. There is an ambiguity here: when you say A::fa(), it still doesn't tell the compiler which A object to use. There isn't any way to access class A. That's what the warning is telling you.
This seems like an awfully strange construct, though. Public inheritance should be used for is-a relationships. You are saying that C is-a A twice over? It doesn't make sense. And that suggests that this is either an artificial example that would never come up in practice, or you should reconsider this design.
You can use virtual inheritance to overcome such problem:
class B : virtual public A {
Now, you can use A::fa() simply in the child class C.
void fc()
{
fa();
}
However, I generally don't see any practical need to inherit class A again into class C, when B is already publically inheriting A. So, In your case, you can make it simple:
class C : public B {
Edit:
If you want 2 instances for A. then the direct instance which you are intending can be made as an object of C:
class C : public B {
A obj;
Because, having a directly inherited A will not be usable in anyway. You cannot declare any pointer or reference to it inside the scope of C.

"Using" directive fails within template

I understand that the member names of a base-class template are hidden within the scope of a derived class, and therefore must be accessed using this->foo or Base<T>::foo. However, I recall that C++ also allows you to use the using keyword, which can come in handy in a derived-class function which frequently accesses a base-class variable. So, in order to avoid cluttering up the function with this-> everywhere, I'd like to use the using keyword.
I know I've done this before, but for whatever reason I can't get it to work now. I'm probably just doing something stupid, but the following code won't compile:
template <class T>
struct Base
{
int x;
};
template <class T>
struct Derived : public Base<T>
{
void dosomething()
{
using Base<T>::x; // gives compiler error
x = 0;
}
};
int main()
{
Derived<int> d;
}
The error, (with GCC 4.3) is: error: ‘Base<T>’ is not a namespace
Why doesn't this work?
It doesn't work because C++ language has no such feature and never had. A using-declaration for a class member must be a member declaration. This means that you can only use in class scope, but never in local scope. This all has absolutely nothing to do with templates.
In other words, you can place your using-declaration into class scope
struct Derived : public Base<T> {
...
using Base<T>::x;
...
};
but you can't have it inside a function.
Using-declarations for namespace members can be placed in local scope, but using-declarations for class members cannot be. This is why the error message complains about Base<T> not being a namespace.
Outside class scope (if you are in a block etc), you can only name namespace members in a using declaration.
If you don't want to place that using declaration into the scope of Derived (which IMO is the favorable solution), your other option is to use a reference
int &x = this->x;
x = 0;
It should be noted that this is semantically different, because it
Forces a definition to exist for Base<T>::x, which might not be the case for static const data members
Forces Base<T>::x to be of type int& or be convertible to type int&.
Otherwise, if you want to avoid using this-> all again, I don't see other options.
template <class T>
struct Base
{
int x;
};
template <class T>
struct Derived : public Base<T>
{
using Base<T>::x;
void dosomething()
{
x = 0;
}
};
int main()
{
Derived<int> d;
}
As others said, it is working only class scope.