Sign in to follow this  

can't understand scope error

This topic is 3856 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi. I have an error that makes no sense to me. There is a (non-template) class CA. Template class CB has a class CA member. Template class CC inherits from CB. For some crazy reason, CC cannot access the CA member from CB, even though it is public, yet it works fine using "this->". (in the test I used structs to make it easier)
struct CA {
    int m_A;
};

template <typename T>
struct CB {
    CA m_CB;
    T m_T;
};

template <typename T>
struct CC : public CB<T> {
    void foo() {
        m_CB.m_A = 1; // This line causes compiler error.
        this->m_CB.m_A = 1; // But this line works.
    }
};

int main() {
    return 0;
}

test:16: error: 'm_CB' was not declared in this scope
struct CA {
    int m_A;
};

struct CB {
    CA m_CB;
    int m_T;
};

template <typename T>
struct CC : public CB {
    void foo() {
        m_CB.m_A = 1;
    }
};

int main() {
    return 0;
}

compiles fine. Any idea what's wrong? Thanks for all assistance. NOTE: I'm using g++ on ubuntu: "g++ test.cpp"

Share this post


Link to post
Share on other sites
You compiler doesn't accept it, that is what is wrong. At least visual studio doesn't throw any errors from your code. But what would fix it? Don't really know, maybe just turning that setting off that causes the error if that is even possble. :)

[Edited by - Arex on May 21, 2007 2:09:38 AM]

Share this post


Link to post
Share on other sites
The reason is to prevent ambiguous binding: you have to specify that an object is a class member (or a type name) when it's inside a template to go around the fact that templates can be specialized.


// A global variable by that same name
CA m_CB;

// Template specialization. It has no member 'm_CB'
template<>
class CB<int> {};

// Your derived class
template <typename T>
struct CC : public CB<T> {
void foo() {
// This line would bind to a different scope (global or
// member) depending on T. It's ambiguous, thus unacceptable.
m_CB.m_A = 1;

// This line always binds to a member, and complains if it
// doesn't exist
this->m_CB.m_A = 1;
}
};



Share this post


Link to post
Share on other sites
At least my Visual Studio 2005 Prof. didn't complain anything (the first code example), do I have some weird settings on that will just ignore that kind of stuff? :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Arex
At least my Visual Studio 2005 Prof. didn't complain anything (the first code example), do I have some weird settings on that will just ignore that kind of stuff? :)


I've just checked, and no, it doesn't. It will just bind to different scopes.

Share this post


Link to post
Share on other sites
Thanks - yea I seem to remember that now, must've come across it before.
And inherited functions are just as bad. Doh.

Pity you can't specify a template as non-overloadable or something.

I guess the best option then is just to go with "this->".


I read that you can also fix it by prefixing the variable "m_CB" with "CB<T>."
(Here, http://www.ps.uni-sb.de/pipermail/gecode-users/2005-February/000805.html)
Although it would have difficulty with overloading, as in ToohrVyk's example.

Didn't work for me though.


Thanks for the explanation.

Share this post


Link to post
Share on other sites
Quote:
Original post by shukapi
Thanks - yea I seem to remember that now, must've come across it before.
And inherited functions are just as bad. Doh.

Pity you can't specify a template as non-overloadable or something.

The solution is to design the code so that the template can always be overloaded without any problem [smile]. If you can't do that, then maybe templating the code was not the right solution in the first place.

Why do you inherit from a template anyway?

Share this post


Link to post
Share on other sites
There are templates that are inherited from in STL. "basic_istream" inherits from "basic_ios". My reason for wanting it is the same.

Just because templates are overloadable doesn't mean all template classes need to handle being overloaded. "basic_ios" can't handle being overloaded if "basic_istream" relies on it.

Sorry for the late reply I was off the net for a while.

Share this post


Link to post
Share on other sites
Quote:
Original post by shukapi
There are templates that are inherited from in STL. "basic_istream" inherits from "basic_ios". My reason for wanting it is the same.

Just because templates are overloadable doesn't mean all template classes need to handle being overloaded. "basic_ios" can't handle being overloaded if "basic_istream" relies on it.

Sorry for the late reply I was off the net for a while.


The fact is that as long as you provide a charT type and a corresponding traits type, the stream classes you listed just work. From a design point of view, they are completely abstract with respect to the data type they handle.

The problem (as stated by Jamie Fristrom in his blog) is that we often use template very badly, to abstract things that do not need to be abstracted. A template class should be designed to accept any possible value as a parameter. If the parameter is a type, then it should accept any type (that satisfy the conditions; of course, some limits have to be set, but they should only deal with the meta type (copy-constructible, default-constructible, ...), not with the type itself. This is the reasoning behind the concept idea that will be implemented in the coming C++ standard).

More important, once a class template has been created, and since it can be specialized by the user, any use of the template class should take that into account. It means that
  1. the correct interface of the template class need to be implemented; a specialization that breaks the interface of the template is utterly useless. And the guy who did it should be killed using a spoon.
  2. any client of this template class should only use this interface, and should not assume any internal knowledge of the template class.
Failing to do that is just catastrophic from a software design point of view, as you have to break numerous rules (the main one being the Open/Closed Principle, as you'll have to take the type into account if you want to use it, which means that you can't write generic code (and not being able to write generic code to use templates is a bit ironic)).

That's not what you do in your example, as you assume an internal knowledge of CB<T> - the member m_CB, which might not be present if CB is specialized. Of course, creating an empty specialization of CB is just as bad as assuming that m_CB is always there. CB<T> defines an interface. CB<int> has to define the same interface - but its implementation can be completely different.

Now, let's return to basic_ios<charT, traits>. If someone decides to specialize basic_ios<> and to remove all functions from it (and, by doing so, breaking the interface used by basic_istream<>), you can say (at least) two things (not to mention that this guy is really stupid): not only he's messing up with the whole standard library (because he changes the definition of basic_ios<>, and this definition is written black-on-white in something called the C++ standard), but he also do something for which he has no idea of the potential effects - his core will be non-portable, mostly because he doesn't control the different implementation of the standard library. See the "kill with a spoon" remark I did earlier.

And you didn't answer my question: why are you inheriting a template class? [smile]

Share this post


Link to post
Share on other sites
I'm not sure I entirely agree with you.


Why am I doing it?
I'm playing with a set of template classes like std::vector type class that works with a fancy type of allocator.

The allocator is able to guarantee maximum page usage of (allocated bytes * X + allocated chunks * Y + Z) / P. P = page size. X <= 2. Y <= 8. Z < 128k.

Basically, an allocator that guarantees that even under worst case fragmentation it will never be less than 50% efficient (minimum efficiency can actually be much higher).

It is physically impossible to do this without minor rearrangement, which occurs during allocations. Classes therefore must be able to accept being told (by a function) that they have been moved from address A to B, so any references can be adjusted, which is a massive change. The challenge now is making a few template examples that are simple, powerful and efficient (like std::vector). They will never be as good as STL, and will require more work when using them, but the minimum memory efficiency guarantee is a bonus.

The allocator works fine, working on the class template examples.



So basically, if it's ok for std::ios to do it, it should be ok for me to do it, since I'm working at that level. I document well enough and the requirements for my base class should be able to be considered a "standard", even if it's only for a proof-of-concept (or possibly very very alpha) library.
Therefore, if anyone screws with my base class you'll have cause to use your spoon.



As for m_CB, can the interface to an object only contain member functions? why not member variables also? Of course I could simply create an access-by-reference function to return the member variable, or just getter/setter functions if the internals need to be flexible - but this template is most definately not intended to be specialised. What do you think? is it bad form to specify a variable in a class interface?



Lastly, it seems to me templates are used more often to allow common functionality for different base types then to allow specialisation of the template. I've only used specialisation (or provided an interface where specialisation is intended) a couple of times, a small fraction of template use. Is this unusual?


Maybe this should've been posted in "general".

Share this post


Link to post
Share on other sites

This topic is 3856 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this