提取模板类的默认参数

时间:2015-03-04 15:54:46

标签: c++ templates c++11 template-meta-programming default-arguments

有没有办法在编译时只知道未经专门化的模板类来提取模板类的默认参数?

我知道如何提取实例化的模板类'参数,如下所示:

// Just an example class for the demonstration
template<class A, class B=void>
struct example {};

// Template parameters storage class
template<class...An>
struct args;

// MPL utility that extracts the template arguments from a template class
template<class C>
struct get_args
{
    typedef args<> type;
};
template<template<class...> class C, class...An>
struct get_args< C<An...> >
{
    typedef args<An...> type;
};

// And the assertion
static_assert(
    std::is_same
    <    args<int,void>,
         get_args< example<int> >::type
    >::value,
    "Check this out"
);

现在我想知道的是,是否可以使用decltype或其他任何内容来仅从非专用模板类中检索默认模板参数:

// MPL utility that extract template default arguments from a template class
template<template<class...> class C>
struct get_default_args
{
    typedef /* what goes here? */ type;
};

// And the assertion
static_assert(
    std::is_same
    <    args<void>,
         get_default_args< example >::type
    >::value,
    "Check this out"
);

目前,我只想出了如何提取模板类的参数数量,而不是它们的默认值:

namespace detail {
    template< template<class> class >
    boost::mpl::size_t<1> deduce_nb_args();
    template< template<class,class> class >
    boost::mpl::size_t<2> deduce_nb_args();
    template< template<class,class,class> class >
    boost::mpl::size_t<3> deduce_nb_args();
    /* ... and so on ... */
}

// MPL utility that extract the number of template arguments of a template class
template<template<class...> class C>
struct get_nb_args :
  decltype(detail::deduce_nb_args<C>()) {};

// And the assertion
static_assert(
    get_nb_args< example >::value == 2,
    "Check this out"
);

修改

似乎最后,再一次,MSVC阻止我执行此操作。 类似下面的内容会导致编译器因致命错误C1001而崩溃:编译器中发生了内部错误。

template<template<class...> class D> static
boost::boost::mpl::int_<0> mandatory(D<>*)
{ return boost::boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<1> mandatory(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<2> mandatory(D<void,void>*)
{ return boost::mpl::int_<0>(); }
template<template<typename...> class D> static
boost::mpl::int_<-1> mandatory(...)
{ return boost::mpl::int_<-1>(); }

int check()
{
  return mandatory<example>(nullptr);
}

尝试下一个导致错误C2976:'D':模板参数太少

template<template<class,class> class D> static
boost::mpl::int_<0> mandatory2(D<>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<1> mandatory2(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<2> mandatory2(D<void,void>*)
{ return boost::mpl::int_<0>(); }

int check2()
{
  return mandatory2<example>(nullptr);
}

所以对我而言,似乎无论采用何种方法,MSVC都禁止使用默认参数对模板类进行编程实例化。 反过来,我似乎不可能使用SFINAE技术来提取: 1.强制参数数量; 2.默认参数的类型。

编辑2

好的,经过多次测试后,当尝试仅使用默认参数以编程方式实例化模板类时,似乎是MSVC出现的错误。

我提交了错误报告here和另一个here

这是一个traits类,允许使用既不会导致编译器崩溃的给定模板参数来检查类是否可实例化,但是对于完全默认的可实例化类不评估为真

namespace detail {
    typedef std::true_type true_;
    typedef std::false_type false_;

    template< template<class...> class D, class...An >
    true_ instantiable_test(D<An...>*);
    template< template<class...> class D, class...An >
    false_ instantiable_test(...);
}
template< template<class...> class C, class...An >
struct is_instantiable : decltype(detail::instantiable_test<C,An...>(nullptr)) {};

话虽如此,MSVC似乎无法检索使用默认参数实例化的模板类型。通常,以下内容无法编译:

template< template<class...> class T, class...An >
struct get_default_v0
{
    typedef T<An...> type;
};

namespace detail {
    template< template<class...> class T, class...An >
    T<An...> try_instantiate();
} // namespace detail

template< template<class...> class T, class...An >
struct get_default_v1
{
    typedef decltype(detail::try_instantiate<T,An...>()) type;
};

// C2976
static_assert(
  std::is_same< get_default_v0<example,int> , example<int,void> >::value,
  "v0"
);

// C2976
static_assert(
  std::is_same< get_default_v1<example,int> , example<int,void> >::value,
  "v1"
);

2 个答案:

答案 0 :(得分:1)

我会尝试这样的事情:

template <typename ...> struct Get2;

template <template <typename...> class Tmpl,
          typename A, typename B, typename ...Rest>
struct Get2<Tmpl<A, B, Rest...>>
{
    using type = B;
};

template <template <typename...> class Tmpl> struct GetDefault2
{
    using type = typename Get2<Tmpl<void>>::type;
};

答案 1 :(得分:1)

我意识到这是一个很长的答案,但这是一种可行的方法:

#include <type_traits>

namespace tmpl
{

namespace detail
{

template<template<typename...> class C, typename... T>
struct is_valid_specialization_impl
{
  template<template<typename...> class D>
  static std::true_type test(D<T...>*);
  template<template<typename...> class D>
  static std::false_type test(...);

  using type = decltype(test<C>(0));
};

} // namespace detail

template<template<typename...> class C, typename... T>
using is_valid_specialization = typename detail::is_valid_specialization_impl<C, T...>::type;

} // namespace tmpl

以下是我github repository的部分复制/粘贴,不要过于担心,大部分代码都是找到所需的最小/最大模板参数数量(在这种情况下我们只关心最小数量):

#if !defined(TEMPLATE_ARGS_MAX_RECURSION)
  #define TEMPLATE_ARGS_MAX_RECURSION 30
#endif

namespace tmpl
{

namespace detail
{

enum class specialization_state {
  invalid,
  valid,
  invalid_again
};

template<bool, template<typename...> class C, typename... T>
struct num_arguments_min
: std::integral_constant<int, sizeof...(T)>
{ };

template<template<typename...> class C, typename... T>
struct num_arguments_min<false, C, T...>
: num_arguments_min<is_valid_specialization<C, T..., char>::value, C, T..., char>
{ };

template<specialization_state, template<typename...> class C, typename... T>
struct num_arguments_max;

template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid, C, T...>
: num_arguments_max<
  is_valid_specialization<C, T..., char>::value
    ? specialization_state::valid
    : specialization_state::invalid,
  C,
  T..., char
>
{ };

template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::valid, C, T...>
: std::conditional<
  ((sizeof...(T) == 0) || (sizeof...(T) == TEMPLATE_ARGS_MAX_RECURSION)),
  std::integral_constant<int, -1>,
  num_arguments_max<
    is_valid_specialization<C, T..., char>::value
      ? specialization_state::valid
      : specialization_state::invalid_again,
    C,
    T..., char
  >
>::type
{ };

template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid_again, C, T...>
: std::integral_constant<int, (sizeof...(T) - 1)>
{ };

} // namespace detail

template<template<typename...> class C>
struct template_traits
{
  constexpr static int args_min = detail::num_arguments_min<is_valid_specialization<C>::value, C>::value;
  constexpr static int args_max = detail::num_arguments_max<is_valid_specialization<C>::value
                                                              ? detail::specialization_state::valid
                                                              : detail::specialization_state::invalid,
                                                            C>::value;

  constexpr static bool is_variadic = (args_max < args_min);

  template<typename... T>
  using specializable_with = is_valid_specialization<C, T...>;
};

} // namespace tmpl

一些专门针对您问题的助手类型:

template<typename... Ts>
struct type_sequence { };

namespace detail
{

template<int N, typename...>
struct skip_n_types;

template<int N, typename H, typename... Tail>
struct skip_n_types<N, H, Tail...>
: skip_n_types<(N - 1), Tail...> { };

template<typename H, typename... Tail>
struct skip_n_types<0, H, Tail...>
{
  using type = type_sequence<H, Tail...>;
};

} // namespace detail

template<int N, typename... T>
using skip_n_types = typename detail::skip_n_types<N, T...>::type;

namespace detail
{

template<typename T>
struct get_default_args;

template<template<typename...> class T, typename... A>
struct get_default_args<T<A...> >
{
  using type = typename skip_n_types<
                          tmpl::template_traits<T>::args_min,
                          A...>::type;
};

} // namespace detail

template<typename T>
using get_default_args = typename detail::get_default_args<T>::type;

把它们放在一起:

template<typename>
struct dependant { };

template<typename T, typename U = void>
struct example { };

template<typename T, typename U = dependant<T> >
struct dependant_example { };

template<typename T>
void print_type(T)
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

int main(int argc, char** argv)
{
  {
    using example_type = example<int>;
    using default_args = get_default_args<example_type>;

    print_type(example_type{});
    print_type(default_args{});
  }
  {
    using example_type = dependant_example<int>;
    using default_args = get_default_args<example_type>;

    print_type(example_type{});
    print_type(default_args{});
  }
}

输出:

void print_type(T) [T = example<int, void>]
void print_type(T) [T = type_sequence<void>]
void print_type(T) [T = dependant_example<int, dependant<int> >]
void print_type(T) [T = type_sequence<dependant<int> >]