构造函数参数的所有组合

时间:2014-01-02 03:34:24

标签: c++

非常可能是一个愚蠢的问题,但无论如何它仍然存在。

是否有一种简短的方法来声明可以采用任何参数组合的构造函数?例如,具有3个args的ctor具有2 ^ 3种可能的组合(如下面的模型中所示)。

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;
};

我可以避免宣布所有不同的ctors吗?

5 个答案:

答案 0 :(得分:4)

您可以使用默认参数:

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来阻止将构造函数用作隐含转换运算符。 (有关详细信息,请参阅this link。)

您可以使用以下任何模式实例化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参数?

答案 1 :(得分:3)

我不是通过构造函数来做,而是通过重载,比如运算符&lt;&lt;。

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.

答案 2 :(得分:3)

另一个选项 - 使用Boost参数库来使用命名参数和默认值,以便指定1,2或3个参数。这种方法的一个优点是您可以按任何顺序提供它们。有关详细信息,请参阅http://www.boost.org/doc/libs/1_55_0/libs/parameter/doc/html/index.html

答案 3 :(得分:1)

使用SFINAE的另一种方法 奖金:所有参数订单都没问题 对T的限制相同:既不能Specie也不能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;
}

答案 4 :(得分:0)

你可以考虑像一个流利的建设者。 (这里我省略了使用模板来简化图片,但我认为你自己在构建器中添加模板参数应该是微不足道的:

(伪代码)

// 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());
  }
};

所以你的构造代码应该是这样的:

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