Thread: [C++11] Delete all parent methods of a given name

  1. #1
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587

    [C++11] Delete all parent methods of a given name

    I'm writing a sorted vector implementation and trying to do it as simply as possible.

    Right now, I'm declaring the sorted vector with a protected subclass of vector, then using the "using" keyword to explicitly inherit all methods that aren't related to adding new elements to the vector (so I can control the order).

    Eg:
    Code:
    template<typename T, class Cmp = std::less<T>>
    class sorted_vector : protected std::vector<T>{
    public:
        typedef typename std::vector<T>::iterator;
    
        using std::vector<T>::operator=;
        using std::vector<T>::assign;
        using std::vector<T>::get_allocator;
    
        using std::vector<T>::at;
        using std::vector<T>::operator[];
    //...
    };
    Obviously, this is annoyingly redundant. So what I'd like to do is something using the new "delete" keyword from C++11. Is there any quick, expressive way of deleting specific methods?

    Also, it's pretty annoying to have to
    typedef base_class::type type
    to inherit a type from a base class. Is there a shorter way to do that?

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Extend the interface of std::vector by providing non-member functions to add elements to a sorted std::vector.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587
    So something like
    Code:
    template<typename T>
    vector<T>::iterator sorted_insert(vector<T> &vec, T &val){
        // do stuff
    }
    Is that what you mean?

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Maybe, but the second parameter should be a const reference. I also wonder if "push_sorted" or something like that would be a better name to keep with the "push_back" and "push_front" naming scheme, since the member functions named "insert" take an iterator as an argument. Then, you might also want to overload with a version in which the second parameter is an rvalue reference.

    Note that std::vector is actually declared more like:
    Code:
    template <class T, class Allocator = allocator<T> > class vector;
    so you should include the allocator template parameter too. There might be more template parameters on a given implementation, but you could ignore that possibility for now.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    If you do go that route, follow in the footsteps `std::make_heap' and friends. You'd have (want) to overload these utility functions with an option for specifying a comparator defaulting to "less than".

    That said, I'm against what laserlight suggested because, ultimately, most people who go this path don't do the work. (The standard library doesn't do the work.) Placing the burdens of a special form of container on a new interface using the same typing significantly complicates generalization. For example, the same type of container template and underlying interface is involved so you can't easily generalize the process of reading a file by entry and storing it into all such specializations of a container. (Here the use of "special" and "specialization" isn't referring to templates.) For those not aware, the standard containers do not support inheritance based polymorphisms so that approach will not work.

    With that in mind, it is possible to do the work correctly by also implementing iterators generalizing over a container's specialty and simply implementing such processes with the relevant iterator abstraction. So, for example, you might have an `sort_insert_iterator' and the associated `sort_inserter' each optionally binding to a specified comparator. In other words, you change the type on which such processes operate through decoration keeping the interface the same by using already established abstractions. (These would be the type of the iterator and the "inserting iterator" respectively.)

    All of that said, I'd recommend just using a class without the inheritance. You can define whatever methods you want and only those methods. You can still "contain" a `std::vector<???>' to do the heavy lifting. With that done, if you implement at least the required methods, existing generalizations (implementations of a process) using the standard container abstraction, like `push_back', will work "out of the box".

    Soma

  6. #6
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    I understand the academic and performance reasons why you might want to implement a sorted vector, but why not just use std::set? its data is already sorted.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  7. #7
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Quote Originally Posted by User Name: View Post
    Obviously, this is annoyingly redundant. So what I'd like to do is something using the new "delete" keyword from C++11. Is there any quick, expressive way of deleting specific methods?
    Isn't vector a container with ~50 members? What does "quick" mean, because I doubt you have typing time in mind?

    The approach "allow listed" has the advantage over "deny listed" such that you are explicit about what member functions are exposed, and you are less likely to accidentally expose something that would break class invariants. This also applies to future changes of vector's public interface, which has already changed a bit since C++11.

    Notice that phantomotap's approach is also "allow listed".

    Quote Originally Posted by Elkvis View Post
    I understand the academic and performance reasons why you might want to implement a sorted vector, but why not just use std::set? its data is already sorted.
    I'm sure he is aware of the existance of sets...
    Last edited by kmdv; 05-07-2013 at 08:56 AM.

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by kmdv View Post
    Isn't vector a container with ~50 members? What does "quick" mean, because I doubt you have typing time in mind?

    The approach "allow listed" has the advantage over "deny listed" such that you are explicit about what member functions are exposed, and you are less likely to accidentally expose something that would break class invariants. This also applies to future changes of vector's public interface, which has already changed a bit since C++11.

    Notice that phantomotap's approach is also "allow listed".
    Likely, it means instead of typing out 50 functions, only type out a handful "disallowed" functions. No such feature exists in C++ to my knowledge.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #9
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Quote Originally Posted by Elysia View Post
    Likely, it means instead of typing out 50 functions, only type out a handful "disallowed" functions. No such feature exists in C++ to my knowledge.
    I know how it is faster regarding typing. I just asked in case I don't get his definition of "quick" (he might have meant "clean"), because to me, copying 50 member declarations does not seem to be as time consuming as implementing the additional members properly.

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Likely, it means instead of typing out 50 functions, only type out a handful "disallowed" functions.
    O_o

    I'd argue that inheriting for implementation only to then specifically list a function as "This does not exist!" is the wrong approach even if the language directly supported `deleted' for arbitrary functions.

    Sure, it takes a few minutes, but it isn't as if it will consume a lot of time to get this same functionality with `std::vector<???>' still doing the work.

    No such feature exists in C++ to my knowledge.
    Well, there is a way, but it is up there with binding a reference to null so I will not discuss it.

    Soma

  11. #11
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    It would appear that you are implementing what already exists in the Loki library:
    Loki: AssocVector.h Source File
    Loki: Loki::AssocVector< K, V, C, A > Class Template Reference

    And yes, a sorted vector does have advantages over a set. Memory usage and lookup speed.

    It looks like they never got around to my suggestion of changing a bulk-insert to do a sort and merge, but it's still a good class worth using.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  12. #12
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587
    Quote Originally Posted by phantomotap View Post
    I'd argue that inheriting for implementation only to then specifically list a function as "This does not exist!" is the wrong approach even if the language directly supported `deleted' for arbitrary functions.]
    Isn't that the point behind inheriting protected? I'm only asking for finer grained control over the visibility of inherited functions. If all protected or all public are viable options, why isn't some protected and some public? "using" semantically says, "my implementation doesn't require any changes to this protected-ly inherited function, so I'll just expose it directly instead of writing a superfluous wrapper".
    So why not have something that says, "this messes with the invariants of my design, don't allow it"?

    By "quick" I mean without so much pointless code for something that should be simple. Clean, as someone else has already said, is a better way to put it.

    Although, I didn't consider the point made by kmdv. It would be more explicit to just list them. And explicit-ness is almost never a bad thing.

    As for writing it without inheritance. That's a viable option. I don't like it because, at the shorted, it requires I right a
    Code:
    class example{
       int method(int a){
          int underlying_type.method(a);
       }
    };
    style method for every method I want to use, which is, IMO, worse than what I'm already doing. And that's the best case because that's assuming I'm using an underlying type that doesn't most of the work. If I did all of it, the shortest method would be longer than "using ...". I also prefer using pre-existing types because it is likely done better than I can do it. I know what I'm doing, and can implement a naive vector in a relatively short amount of time. But I'd assume they probably use some heuristics for preallocation and other optimizations that would be a pain to rewrite. That and it's a lot of work for nothing.
    Last edited by User Name:; 05-07-2013 at 09:56 PM.

  13. #13
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    You should look at the links I posted.
    The AssocVector uses private inherritance of std::vector, and since it implements exactly what you are after, there's a good chance that means you would at the very least want to do the same thing.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  14. #14
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Isn't that the point behind inheriting protected?
    O_o

    No.

    Using `protected' inheritance says `public' and `protected' methods of the base class are `protected'.

    You aren't saying "This method is fine." and "This method breaks invariants." with `protected' inheritance. You are saying "The following methods are available with normal restrictions.". That is a different beast from saying "The following method does not exist in the interface for this class.". Code is written for people; you should document, by virtue of the value of canonical semantics the interface you make available.

    With `protected' inheritance, you are saying that only derived classes should depend on an interface. I believe you are shooting for "The following interface should never be used.". You should do that by not doing that at all; you should say "The following is the interface for this object."; you do that, in this case because you are using another object to do much of the work, by containment or `private' inheritance with `using' directives.

    I'm only asking for finer grained control over the visibility of inherited functions.
    I know what you are asking.

    What you are asking for isn't problematic; that exists, and it is useful.

    My problem is how you are asking to accomplish what you wish to accomplish.

    If all protected or all public are viable options, why isn't some protected and some public?
    There is absolutely nothing wrong with what you describe. I did not suggest otherwise.

    I'm saying that the intended interface should be directly specified using canonical semantics.

    So why not have something that says, "this messes with the invariants of my design, don't allow it"?
    Why are you saying "This interface is available to derived classes." if your class design doesn't allow for them? That's the problem I have with this scenario. You are saying something with the code that you are clearly not intending to say.

    For the sake of argument though, let's assume that a fourth form of inheritance was available. (You may call it whatever you like.) I'd still argue that listing a function in the interface as "The following interface does not exist in this object." has poor semantic value versus saying "The following is the interface for this object.".

    By "quick" I mean without so much pointless code for something that should be simple. Clean, as someone else has already said, is a better way to put it.
    I do not perceive either method as having any "pointless code". (I believe you are using the wrong form of inheritance, but the method of inheritance paired with `using' directives is fine.) I've worked with a lot of libraries that have awful documentation. In some cases, the headers are all you have available. Sometimes the headers are also awful. Sometimes the headers are great. The code you suggest as "pointless" provides valuable documentation of the intended interface.

    I know what I'm doing, and can implement a naive vector in a relatively short amount of time. But I'd assume they probably use some heuristics for preallocation and other optimizations that would be a pain to rewrite. That and it's a lot of work for nothing.
    Well, that's all really besides the point I was making, and it certainly has nothing to do with my suggestion, but with that in mind, I too suggest you read the link iMalc provided. I no longer use "Loki", but I did for a long time. It has some great facilities.

    Soma

    Soma

  15. #15
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Boost.Container has flat_set and flat_map, which are implemented in terms of a dynamic array, but provide the interface of an ordered associative container, which means that, iterator validity guarantees aside, they are a drop-in replacement for std::set and std::map, respectively.
    Chapter 7. Boost.Container - 1.53.0

    As for protected inheritance, that's a weird little thing that exists more out of a sense of completeness (you have public, protected and private for members, you should have all three for bases) than for any particular use case. I have never found a good use for it. If you really want implementation inheritance, use private. If you can, avoid even that in favor of containment.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Can you inherit Methods into other methods?
    By bobbelPoP in forum C++ Programming
    Replies: 5
    Last Post: 08-02-2008, 08:32 AM
  2. Concerning delete/delete[] at program exit
    By laserlight in forum C++ Programming
    Replies: 58
    Last Post: 01-09-2008, 01:40 PM
  3. Replies: 17
    Last Post: 11-16-2006, 09:06 PM
  4. Calling parent methods
    By Tonto in forum C++ Programming
    Replies: 2
    Last Post: 10-24-2006, 07:00 PM
  5. Parent window
    By maxorator in forum Windows Programming
    Replies: 4
    Last Post: 08-27-2006, 12:22 PM