All combinations of constructor arguments

Most likely, a dumb question, but here everything goes.

Is there a short way to declare constructors that accept any combination of arguments? For example, a ctor with 3 arguments has 2 ^ 3 possible combinations (as can be seen from the layout below).

template<typename T>
struct Node{
    Node(Species sp, Edge edge, T data) : sp_m(sp), edge_m(edge), data_m(data) { }

    // Default Ctor, 0 arg
    Node() : Node(Species(), Edge(), T()) { }

    // All combinations of 1 arg
    Node(Species sp): Node(sp,          Edge(), T())  { }
    Node(Edge edge) : Node(Species(),   edge,   T())  { }
    Node(T data)    : Node(Species(),   Edge(), data) { }

    // All combinations of 2 args
    Node(Species sp, Edge edge) : Node(sp,        edge,   T()) { }
    Node(Species sp, T data)    : Node(sp,        Edge(), data){ }
    Node(Edge edge, T data)     : Node(Species(), edge,   data){ }

    Species sp_m;
    Edge    edge_m;
    T       data_m;
};

Can I avoid declaring all the different ctors?

+4
source share
5 answers

Another method using SFINAE.
Bonus: all arguments are in order. The same restriction on T: there can be neither Specie, nor Edge.

#include <tuple>

// count the number of T in Ts...
template <typename T, typename ...Ts> struct count_type;

template <typename T, typename Tail, typename ...Ts>
struct count_type<T, Tail, Ts...>
{
    constexpr static int value = std::is_same<T, Tail>::value + count_type<T, Ts...>::value;
};
template <typename T> struct count_type<T> { constexpr static int value = 0; };

// index of T in Ts..., or -1 if not found
template <typename T, typename ... Ts> struct get_index;

template <typename T> struct get_index<T> { static const int value = -1; };

template <typename T, typename ... Ts> struct get_index<T, T, Ts...> { static const int value = 0; };

template <typename T, typename Tail, typename ... Ts>
struct get_index<T, Tail, Ts...>
{
    static const int value =
        get_index<T, Ts...>::value == -1 ? -1 : 1 + get_index<T, Ts...>::value;
};

// similar to get<T>(tuple), but return T{} if not found
template <typename T, int N, typename ... Ts>
struct get_helper
{
    static T get(const std::tuple<const Ts&...>& t) { return std::get<N>(t); }
};

template <typename T, typename ... Ts>
struct get_helper<T, -1, Ts...>
{
    static T get(const std::tuple<const Ts&...>& t) { return T{}; }
};

// similar to get<T>(tuple), but return T{} if not found
template <typename T, typename ... Ts>
T get_or_construct(const std::tuple<const Ts&...>& t)
{
    return get_helper<T, get_index<T, Ts...>::value, Ts...>::get(t);
}


class Species {};
class Edge {};

template<typename T>
struct Node{
    Node(const Species& sp, const Edge& edge, T data) : sp_m(sp), edge_m(edge), data_m(data) { }

    template <typename ... Ts>
    Node(const Ts&... ts) : Node(std::tie(ts...)) {}

private:
    template <typename ... Ts, typename =
        typename std::enable_if<count_type<Species, Ts...>::value <= 1
                                && count_type<Edge, Ts...>::value <= 1
                                && count_type<T, Ts...>::value <= 1>::type>
    Node(const std::tuple<const Ts&...>& t) :
        Node(get_or_construct<Species>(t),
             get_or_construct<Edge>(t),
             get_or_construct<T>(t))
    {}

private:
    Species sp_m;
    Edge    edge_m;
    T       data_m;
};

int main(int argc, char *argv[])
{
    Node<int> n(Species {}, Edge{}, 42); // normal constructor
    Node<int> n2(Species {}, 42);        // template constructor
    return 0;
}
+1
source

You can use the default options:

template<typename T>
struct Node{
    explicit Node(Species sp = Species(), Edge edge = Edge(), T data = T()) :
        sp_m(sp), edge_m(edge), data_m(data) { }

    Species sp_m;
    Edge    edge_m;
    T       data_m;
};

explicit, . (. .)

Node :

int main()
{
    Species s;
    Edge e;
    int i;

    Node<int> x1 = Node<int>();

    Node<int> x2 = Node<int>(s);
    Node<int> x3 = Node<int>(s, e);
    Node<int> x4 = Node<int>(s, e, i);

    // Can't skip parameters, so supply default parameters when needed:
    Node<int> x5 = Node<int>(Species(), Edge(), i);
}

:

Node<int> x6 = Node<int>(i);

, , , , :

Node<Species> x6 = Node<Species>(s);

s sp data?

+4

, , , <.

struct Node {

   Node() : Node(Species(), Edge(), T()) { }

   Node& operator << (Species sp) {...}
   Node& operator << (Edge edge) { ... }
   Node& operator << (T data) { ... }

}

:

Node n1; n1 << species << edge;
Node n2; n2 << edge;
etc.
+3

- . ( , , , :

( psuedo)

// Have only 1 ctor for Node, or simply no ctor as it is a struct

struct Node {
   Species m_sp;
   Edge    m_edge;
   Data    m_data;
   Node(Species& sp, Edge& edge, Data& data) : m_sp(sp), m_edge(edge), m_data(data) { }
};

class NodeBuilder {
private:
  Species* m_species;
  Edge* m_edge;
  Data* m_data;

public:
  static NodeBuilder& newBuilder() {
    return NodeBuilder();
  }

  void withSpecies(Species& species) {
    m_species= &species;
  }
  void withEdge(Edge& edge) {
    m_edge = &edge;
  }
  void withData(Data& data) {
    m_data = data;
  }
  void Node& toNode() {
    return Node(m_species? (*m_species) : Species(), 
                m_edge? (*edge) : Edge(),
                m_data? (*data) : Data());
  }
};

So your build code should look like this:

Node node = NodeBuilder.newBuilder()
              .withSpices(someSpices)
              .withData(someData)
              .toNode();
0
source

All Articles