Re: Templates, inheritance and variable visibility
* Jeff Schwab:
Ian Collins wrote:
Alf P. Steinbach wrote:
The definition or existence of 'a_' depends on template parameter.
You have to tell the compiler in some way, by qualification or
'using', that it comes from the base class and is not from an
enclosing scope.
Exactly why that rule was chosen instead of the for C++98 more
reasonable of having to qualify use of enclosing scope names is a
mystery.
How would you qualify a name as "from the enclosing scope?" Within the
current language, I think you'd have to hard-code the entire sequence of
namespaces for each identifier, from global scope on down. If you so
much as wrote std::cout, the compiler would get confused, since it would
have to assume std was defined in the base class.
Uhm, there's both a good point and a bad point (or un-point).
The good point: what to do about about e.g. std::cout.
The bad point: imagining an impractical non-solution and using that as argument.
The possible existence of impractical ways of doing X doesn't imply that X is
impractical. So in terms of reasoning and argumentation it's a fallacy, even
when assuming for argument that the purported impractical way is impractical.
Regarding those outer scope names, the practical consideration is just what you
need most often: do you most often need to refer to base class names (such as
types!), or do you most often need to refer to enclosing scope names.
IME one overwhelmingly most often needs to refer to base class names.
And even with nothing more than the facilities of C++98 it's no big deal to write
template< typename T > class Foo
{
void boo() const
{
using namespace std;
cout << "so there" << endl;
}
};
In this context there's generally no need to qualify like ::std because it's
known to be a namespace name so no ambiguity (unless the class, perversely, is
defined in a namespace that contains a namespace called 'std').
But what about, for example, routine result types?
Well, thinking positively about this, namely how to create a practical solution
instead of how to find some hopefully unworkable non-solution, this is a case
for allowing namespace using directives at class scope, like
template< typename T > class Foo
{
using namespace std;
ostream& boo() const
{
cout << "so there" << endl;
return cout;
}
};
One might then argue that scopes simply shouldn't be nested to the degree that
the above becomes impractical. But I think that would be like someone dying of
thirst in a desert, 12 meters from an oasis, because the person reasons that one
should simply not set out on a trip of more than X hundred meters, so better
stop here, at X hundred meters already crawled, and die. Rejecting that
defaitist and irrational mode of thinking, then, what's lacking in C++ is a way
to refer relatively to scopes, like you can do with directories.
More generally, what's lacking is a /general path notation/ for scopes,
considering that such can involve (a) both namespaces and classes, possibly for
full generality also blocks, and (b) relative designations like "..".
I'd like something like
template< typename T > class Foo
{
using namespace $; // Enclosing namespace
void boo() const
{
say( "so there" );
}
};
The idea being that $, not currently used in standard C++ outside string and
character literals, designates a current scope (of some implied kind), and $$
designates a (lexically) parent scope, like "." and ".." for directories.
Hm, neat! :)
Note in this regard: the Euro symbol isn't part of ASCII or original EBCDIC
(actually I don't know about $ and EBCDIC, too lazy to check!), but $ is.
To you and me both. Although James has cited some corner cases where
it does make sense, I'm still not convinced. It leads to some pretty
ugly and unnecessarily verbose code.
At the time the derived class is defined, the base class hasn't
necessarily been seen yet. There is no way for the compiler (or the
human reader) to know whether the base defines a_, no matter what the
base class's primary template defines. The enclosing scope, OTOH, is
already available.
As pointed out by Ian Collins else-thread, this argument falls on the spears of
existing old compilers that had no problems dealing with the situation.
For there is no more technical problem with an explicitly expressed assumption
than with an implicit assumption.
How could there be?
No matter whether it's explicitly expressed or not, the compiler has to deal
with it. If it can deal with the explicitly expressed assumption, it can deal
with the implicit one. If it can't deal with the assumption in general then C++
template programming is impossible -- but hey, lots of counter-examples! :-)
The problem with explicitly expressed assumption is that it's a heck of a lot
more to write, an extreme verbosity and an extreme visual clutter, not to
mention the work in adding in all this stuff, try it out by compiling, adding
more, and so on, including having to write silly extremely redundant things like
typedef FooFoo< U, V > Base;
typedef typename Base::Value Value;
typedef typename Base::Index Index;
and so on.
Talk about COBOL reincarnation!
Or PL/1!
Cheers, & hth.,
- Alf
--
Due to hosting requirements I need visits to <url: http://alfps.izfree.com/>.
No ads, and there is some C++ stuff! :-) Just going there is good. Linking
to it is even better! Thanks in advance!