Sean Parent

Sean Parent SeanParent

Niner since 2013

Comments

  • Inheritance Is The Base Class of Evil

    @Fuz:I've discussed this issue with several folks on the committee in the past, the consensus is that it is guaranteed because it isn't explicitly disallowed. From Howard Hinnant:

    ---

    Yes, it is allowed and guaranteed to give the expected answer.  There's not a particular place I can quote.  The general reasoning is that nowhere in the standard does it say it is disallowed.  Compare that with Table 100:

    a.insert(p, i, j)

    which says:

    pre: i and j are not iterators into a.
    Inserts copies of elements in [i, j) before p

    It is pretty easy for implementations to guarantee this behavior:

    In the has-capacity-case it is very easy to see there is no problem.

    In the doesn't-have-capacity-case, the new buffer must be created and filled and all the implementation has to do is to make sure the new value is constructed prior to deleting the old buffer.

    vector.insert(p, v[i]) is more interesting to implement, but is also guaranteed to work.

    ----

  • Inheritance Is The Base Class of Evil

    @Will:From how the current wording is phrased, I believe it is still an issue. The problem is the use of the term move assignment operator in the added section of 12.8.20 instead of using is_move_assignable. I have not been following the issue with the committee and do not know if there is an objection to changing the wording to be concept based.

  • Inheritance Is The Base Class of Evil

    @MarkusWerle:Much the same way - but I build the lookup table as just a static table harvested from the code sorted by the type name. Another approach I've used for messages is to defer deserialization until code extracts the value (at which point the class being extracted is known). It avoids the global registry but requires that the code extracting has full information about the type.

    The global registry problem is a classic "there is no good way to do that in C++" problem.

  • Inheritance Is The Base Class of Evil

    @sutm: The answer given by @bkuhns: stack overflow is correct. I would phrase it a bit differently. The axioms of copy are that copies are equal and modifying the copy does not modify the original. By convention, a const value is not modified in any apparent way (though there are many ways that const can be circumvented, doing so violates convention). So as long as a const reference to a value has a lifetime at least as long a the reference, a const reference has value semantics.

    This is why we are able to pass argument by const& to avoid a copy. The caller guarantees, by convention, that the value is not modified and will survive for the duration of the call. With a shared_ptr, the lifetime of the object is ensured by the ref-counted pointer.

    There is a subtle difference with equality - comparing share_ptr for equality, with respect to the shared object, is comparing identity. This doesn't violate the axioms of copy - copies are equal because they are identical, but is typically not the desired comparison. But since we don't expose the "raw" shared_ptr from our object_t, we can trivially provide an equality operator on object_t which returns true iff the contained objects are equal.

    Element of Programming (EoP) has a great discussion on the topics of copy and equality and precisely defines the semantics of both operations. You might also look at this paper:

    http://www.stepanovpapers.com/DeSt98.pdf

    I believe this was the first paper to define the term "regular type" - and I still consider it a must-read.

     

  • C++ Seasoning

    @Chase:I answered your question in the comment section for my other talk:

    http://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil

    Search for "operator=" on that page.

  • Inheritance Is The Base Class of Evil

    @McHalls:It works like this:

    • When the instance of my_class_t is placed into the document, an object_t is constructed through the template constructor.
    • The constructor instantiates an instance of model<my_class_t>
    • The instance of model<my_class_t> has a virtual draw method, that forwards to the draw function taking a my_class_t instance
    • The object_t is not a templated type, but holds a pointer to a concept_t - from which the model<> is derived. The technique is known as "type-erasure" and is used by std::function<>, boost::any, and other libraries.

    So long as there is a stand alone draw function (or the class is serializable through stream out - so it picks up the default implementation of draw), then it can be stored in an object_t, and into the document. No (client-side) inheritance required.

  • C++ Seasoning

    @jerry:

    Answer 1: For best performance pass by universal reference or supply all permutations of r-value/l-value. But profile first - it usually will do no better than pass by value and may end up slower since you have more code polluting your icache. Either approach adds size and complexity to your code - you might be better off investing in other optimizations.

    Answer 2: Use std::stable_sort() to obey the previous ordering(s). For example, let's say the user clicked on the "state" column - you would sort by state as:

    stable_sort(begin(a), end(a),
            [](const person& x, const person& y){ return x.state_ < y.state_; });

    The the user clicked on the "first" column - you would sort by first as:

    stable_sort(begin(a), end(a),
            [](const person& x, const person& y){ return x.first_ < y.first; });

    The result would be as in your subrange sorted example - but the idea extends to any number of columns.

  • Inheritance Is The Base Class of Evil

    @Joe:Yes - that is the correct citation in the standard. The version of clang I use obeys the rule - but gets the trait wrong, which makes things even worse. I posted a test case with my results here:

    https://gist.github.com/sean-parent/6612672

  • Inheritance Is The Base Class of Evil

    @tomkirbygreen:I don't have a "coding guidelines" document in text form. The work from STLab can be found at stlab.adobe.com where you can find the ASL libraries. There are also docs, papers, and presentations on the wiki: stlab.adobe.com/wiki. I also can't recommend working your way through "Elements of Programming" enough.

    The STLab has been gone for a few years now - and ASL has been decaying a bit. I'm trying to get development moved over to github, and you can find the latest code here github.com/stlab/legacy (the reason it is in a "legacy" repo is that the plan is to clean it up and break it into seperate libraries that will go into the adobe-source-libraries repo and/or get submitted to boost).

    I'm also looking for a decent blog space to make some of my coding tips public - github pages hasn't been worked for me in my attempts to get that going.

    Jaakko Järvi and I have collaborated on a number of articles (we're working on another now) and he and his students have done some related work - you can find more papers here: http://faculty.cs.tamu.edu/jarvi/publications/Author/JARVI-J.html

    You can also Google me to find some other video presentations that have been recorded over the years.

  • Inheritance Is The Base Class of Evil

    @Joe:The issue is that if you took your class Foo [for clarity let's write your assignment as:

    Foo& operator=(Foo o) noexcept { member = move(o.member); return *this; }

    ] and put it in a struct:

    struct wrap { Foo m_ };

    Then wrap will not get a default move assignment. For wrap to get a default noexcept move assignment, all members must have a noexcept move assignment - this determination is made by signature. That is the standard says that for wrap to get a default noexcept move assignment all members must have a move assignment with the signature T& operator=(T&&) noexcept.

    The fix is to rephrase the requirement so it says that a struct or class will get a default noexcept move assignment if all members satisfies  is_nothrow_move_assignable<T> - which the above does. That is, we want to define the requirement in terms of the concept, or operation semantics, and not in terms of matching an exact signature.

  • C++ Seasoning

    @depths:There is an optional slide I didn't show that discusses the issue. It is on page 209 in this slide deck <http://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil>. That page also shows an implementation using a lambda instead of not1() to do the predicate negation. I'm hoping that C++14 fixes not1().

    In case you have trouble viewing the slide - here is the code:

    template <typename I, // I models BidirectionalIterator
               typename S> // S models UnaryPredicate
    auto gather(I f, I l, I p, S s) -> pair<I, I>
    {
         using value_type = typename iterator_traits<I>::value_type;
         return { stable_partition(f, p, [&](const value_type& x){ return !s(x); }),
                  stable_partition(p, l, s) };
    }

  • Bjarne Stroustrup - The Essence of C++: With Examples in C++84, C++98, C++11, and C++14

    @Ishan:The "Palo Alto TR" was the result of a workshop to revisit the design for Concepts after the feature was cut from C++11. The resulting paper can be found here <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf>.

View All