Вы можете использовать набор классов тегов, чтобы предоставить имена для каждого параметра времени компиляции и позволить пользователям предоставлять только те параметры, которые они хотят, и в любом порядке:
// In your header file:
#include <type_traits>
namespace FooArgs {
template <typename T> struct T1 {};
template <typename T> struct T2 {};
template <int N> struct X1 {};
// ...
namespace detail {
template <typename Enable, class Matcher, unsigned int N, class... Tags>
struct match_at_most_enable;
template <class Matcher, unsigned int N>
struct match_at_most_enable<void, Matcher, N>
: std::true_type {};
template <class Matcher, unsigned int N, class Tag1, class... Tags>
struct match_at_most_enable<
typename std::enable_if<!Matcher::template match<Tag1>::value>::type,
Matcher, N, Tag1, Tags...>
: match_at_most_enable<void, Matcher, N, Tags...>::type {};
template <class Matcher, unsigned int N, class Tag1, class... Tags>
struct match_at_most_enable<
typename std::enable_if<Matcher::template match<Tag1>::value>::type,
Matcher, N, Tag1, Tags...>
: match_at_most_enable<void, Matcher, N-1, Tags...>::type {};
template <class Matcher, class Tag1, class... Tags>
struct match_at_most_enable<
typename std::enable_if<Matcher::template match<Tag1>::value>::type,
Matcher, 0, Tag1, Tags...>
: std::false_type {};
template <class Matcher, unsigned int N, class... Tags>
using match_at_most = match_at_most_enable<void, Matcher, N, Tags...>;
template <class... Tags> struct inherit_all : Tags... {};
template <class... Tags>
constexpr inherit_all<Tags...>* combine() { return nullptr; }
template <template<typename> class TT, typename DfltType, class... Tags>
struct get_type_helper {
template <class Tag>
struct match : std::false_type {};
template <typename T>
struct match<TT<T>> : std::true_type {};
static_assert(match_at_most<get_type_helper, 1, Tags...>::value,
"An argument tag was specified more than once");
template <typename T>
struct wrap { using type = T; };
template <typename T>
static wrap<T> select(TT<T>*);
static wrap<DfltType> select(...);
using type = typename decltype(select(combine<Tags...>()))::type;
};
template <template<typename> class TT, typename DfltType, class... Tags>
using get_type = typename get_type_helper<TT, DfltType, Tags...>::type;
template <typename T, template<T> class TT, T DfltValue, class... Tags>
struct get_value_helper {
template <class Tag>
struct match : public std::false_type {};
template <T Value>
struct match<TT<Value>> : public std::true_type {};
static_assert(match_at_most<get_value_helper, 1, Tags...>::value,
"An argument tag was specified more than once");
template <T Value>
static constexpr T select(TT<Value>*) { return Value; }
static constexpr T select(...) { return DfltValue; }
static constexpr T value = select(combine<Tags...>());
};
// Note if using C++17 or later, get_value could be a
// constexpr variable template instead of a function.
template <typename T, template<T> class TT, T DfltValue, class... Tags>
constexpr T get_value()
{ return get_value_helper<T, TT, DfltValue, Tags...>::value; }
}
}
template <class... Tags>
int foo(/*params*/)
{
using namespace FooArgs::detail;
using T1 = get_type<FooArgs::T1, Type1, Tags...>;
using T2 = get_type<FooArgs::T2, Type2, Tags...>;
constexpr int X1 = get_value<int, FooArgs::X1, DefaultX1, Tags...>();
// ...
}
// Example usage:
void bar() {
int n = foo<FooArgs::X1<42>, FooArgs::T1<int>>();
}
Обратите внимание на все в FooArgs::detail
является довольно общим, поэтому, если вы хотите использовать этот шаблон с более чем одним набором тегов, вы можете переместить все это в какой-то другой заголовочный файл и сделать get_type
и get_value
доступными с некоторыми более описательными именами или в некоторыхимя с соответствующим именем.