Skip to content

Classes and Structs

Hannes Hauswedell edited this page Mar 6, 2017 · 19 revisions

General Design

POD/Aggregate preferred (shall be struct, otherwise shall be class and always specify rule of 5/6 constructors (ok to = default or = deleted, but always make explicit).

struct my_aggregate_type
{
  int my_member{7}; // use direct member initializers if necessary
};

class my_non_aggregate_type
{
// always specify public, protected, private in this order
public: 
    // Rule-of-six is usually desired
    my_non_aggregate_type() = default;
    // Rule-of-five    
    constexpr my_non_aggregate_type(my_non_aggregate_type const &) = default;
    constexpr my_non_aggregate_type(my_non_aggregate_type &&) = default;
    constexpr my_non_aggregate_type & operator =(my_non_aggregate_type const &) = default;
    constexpr my_non_aggregate_type & operator =(my_non_aggregate_type &&) = default;

protected:
    // make destructor protected to prevent dynamic polymorphism (unless explicitly desired)
    constexpr ~my_non_aggregate_type() = default;
private:
    int my_member{7}; // use direct member initializers if necessary
};

Static polymorphism (via concepts)

Static polymorphism is implemented via C++ concepts. Instead of designing a class, you first abstract the class's public interface and make a concept of it. Algorithms and other free function than work on general types (as templates) that are constrained via more or less specialized concepts.

#include <iostream>
#include <type_traits>

template<typename number_type>
concept bool number_concept = std::is_arithmetic<T>::value;

template<typename number_type>
concept bool integral_concept = number_concept<T> && std::is_integral<T>::value;

template<typename number_type>
    requires number_concept<number_type>
number_type double_the_value(number_type const in)
{
    std::cerr << "general funcion!\n";
    return in * 2;
}

template<typename number_type>
    requires integral_concept<number_type>
number_type double_the_value(number_type const in)
{
    std::cerr << "special funcion!\n";
    return (in << 1);
}

template<typename number_type>
    requires number_concept<number_type>
void print2(number_type const & v)
{
    std::cout << double_the_value(v) << '\n';
}

int main()
{
    print2(double{0.2}); // general case
    print2(int{2});      // specialized case
    print2(long{2});     // specialized case

    return 0;
}

Dynamic polymorphism

In some cases you just don't know which function can be picked at compile-time, e.g. the exact input-file type cannot be known. In these cases you can use an tag-dispatching over an inner type that also follows a concept for these specializations. Tag-dispatching has become quite easy in C++17 with std::visit.

Clone this wiki locally