Is this a valid and correct skeleton class/struct I have been watching Alexander Stepanov programming C++ with components and he, as usual, has some strong opinions. I did like one of his assertions that we use regular, semi-regular and fully ordered types. So I decided to pull together a skeleton class (pair in this case) and wonder if there are any valid reasons for using this skeleton or something different. I realise there is no silver bullet and am not looking for one, but I am interested in the basics being programmatically consistent and correct. Alex seemed to loosely suggest that such a start position would be valid.

I hope this is a valid question, I think some of the areas of concern are:


Is it valid to refer to class as regular/semiregular/fullyordered?
(It's hard to find examples.) 
Are there strong reasons for non-member friend/non-friend member functions? (I'm thinking here of the Dr.
Dobbs article showing non-member (non-friend) functions improving encapsulation.)
Should friend requirements define member/non member functions?
Is it a good rule that each of these groups are implemented fully if at least one member of that group is? I.e. == must also be implemented with != etc. Sort of as an extension to the big 4 or 5 or 6 construction or regular methods :-) 


This is C++11 that MSVC 2012 can handle. Clang, gcc etc. can improve this loads, but for now I am using MSVC 2012 as the baseline. friend is not required in these functions. I have included it to try and cover a normal "template" (if I create a template/snippet) for use where there are private data members. My intent is not to use this as a snippet, though.

#ifndef MY_CLASS_H
#define MY_CLASS_H

#include &lt;tuple&gt;
#include &lt;utility&gt;

template &lt;typename T, typename U&gt;
struct MyClass {     
    T first;
    U second;

    // semiregular

    MyClass(const MyClass&amp; other)
        : first(other.first),
          second(other.second)
    {}

    MyClass(MyClass&amp;&amp; other)
        : first(std::move(other.first)),
          second(std::move(other.second))
    {}

    MyClass()
        : first(),
          second()
    {}

    ~MyClass()
    {}

    MyClass&amp; operator=(MyClass other) {
        swap(*this, other);
        return *this;
    }

    // regular
    friend
    bool operator==(const MyClass&amp; lhs, const MyClass&amp; rhs) {
        return std::tie(lhs.first, lhs.second)
                   == std::tie(rhs.first, rhs.second);
    }

    friend
    bool operator!=(const MyClass&amp; lhs, const MyClass&amp; rhs) {
        return !operator==(lhs, rhs);
    }

    // fully ordered
    friend
    bool operator&lt;(const MyClass&amp; lhs, const MyClass&amp; rhs) {
        return std::tie(lhs.first, lhs.second)
                   &lt; std::tie(rhs.first, rhs.second);
    }

    friend
    bool operator&gt;(const MyClass&amp; lhs, const MyClass&amp; rhs) {
        return operator&lt;(rhs, lhs);
    }

    friend
    bool operator&lt;=(const MyClass&amp; lhs, const MyClass&amp; rhs) {
        return !operator&gt;(lhs, rhs);
    }

    friend
    bool operator&gt;=(const MyClass&amp; lhs, const MyClass&amp; rhs) {
        return !operator&lt;(lhs, rhs);
    } 
};

// swap
void swap(MyClass&amp; lhs, MyClass&amp; rhs) /* noexcept */ {
    using std::swap;
    swap(lhs.first, rhs.first);
    swap(lhs.second, rhs.second);
}


#endif // MY_CLASS_H
It is kind of hard to tell what you're really asking. Without knowing what you're asking, it's hard to provide a helpful and meaningful answer. I will try regardless. Feel free to point me in the right direction if I'm missing the point.




  Is it valid to refer to class as regular/semiregular/fully ordered?


While I'm not familiar with the terminology, the distinctions are useful. These classifications let you reason about a class. For example, std::list&lt;T&gt;::sort() requires that operator&lt; is defined for T. This is part of what Java and C# interfaces and the suggested C++ concepts feature is about. Note that they aren't necessarily accumulative: A type can be "fully ordered" without being "semi-regular".

However, I can't see a good reason for sorting types into only these categories. Stepanov may have some deeper insight here.




  Are there strong reasons for non-member friend/non-friend member functions?


Like Scott Meyers points out in the article you link to, non-member non-friendfunctions improve encapsulation, which leads to looser coupling. (Looser coupling in turn leads to better complexity handling, replaceable components, more testable interfaces and so on. These are good things.)

Regarding non-member friend functions, I personally consider them a code smell. It doesn't necessarily mean that the code is dirty, but in most cases you don't want friends. (Even without friends, you can still throw party;!) Declaring a friend breaks encapsulation and leads to tighter coupling, which is the opposite of what we want.

(If you do have friends, make sure they interact with an interface (i.e. member function) and not directly with private variables/implementation details.)




  Should friend requirements define member/non-member functions?


I am reading the question as: "Should functions that need to be declared friend be member functions instead?"

For functions that are not part of the class' public interface, the answer is yes, definitely. They should be private member functions.

For functions that are part of the public interface: Unless you have a specific reason to make that function non-member, it should be a member function. The point Scott Meyers is making, however, is this: If the function can use the class' public interface instead of being non-member friend or a member function, it should do so: It should be a non-member, non-friend function that interacts with the type through the type's public interface.

My basic guideline is this: Can the function make use of only the public interface? Then make it non-member, non-friend. If not, make it a member function. However, don't extend the public interface more than otherwise necessary just to allow a function to be non-member, non-friend. If you have to extend the public interface for just a single function, then that function should probably be a member function instead.




  Is it a good rule that each of these groups are implemented fully if at least one member of that group is?


When defining one of assignment operator, destructor or copy constructor, the Rule of Three comes into play and you should probably define the others.

For the categories Stepanov talks about: Yes, if you implement one part of the groups of operators, you should probably implement the others. This is because you should follow the Principle of Least Astonishment. Users of a type expect that if you can say a == b, then you should be able to say a != b as well.

The exception is what he refers to as regular: A type that is constructable may not need to be copyable or movable.