非常可能是一个愚蠢的问题,但无论如何它仍然存在。
是否有一种简短的方法来声明可以采用任何参数组合的构造函数?例如,具有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吗?
答案 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();