An other method using SFINAE.
Bonus: all argument orders are fine.
Same restriction on T
: cannot 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;
}