【发布时间】:2015-03-04 15:54:45
【问题描述】:
有没有办法在编译时只知道非特化的模板类来提取模板类的默认参数?
我知道如何提取实例化模板类的参数,如下所示:
// 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' : too little template arguments
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 的一个错误。
这是一个特征类,允许使用给定的模板参数检查一个类是否可实例化,不会导致编译器崩溃,但对于完全默认的可实例化类,不会评估为真。
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"
);
【问题讨论】:
-
好吧,你可以在前面用 n
voids 伪造实例化,每次尝试 SFINAE 失败时,如果它实例化将它传递给一个参数提取器,该提取器在第一个 n 之后提取类型?担忧:如果不是足够早的失败,SFINAE 就会失败。但它可能会奏效。 ...或@KerrekSB 下面的建议 -
另请注意,模板默认参数可以依赖于非默认参数(参见例如
std::vector)。 -
我尝试了类似的方法,但是在尝试解决 sfinae 时 MSVC 崩溃了......也许我做得不对,但是 MSVC 让调试变得非常困难:(
-
毕竟,一切都取决于编译器能够以编程方式使用默认参数实例化模板类型。似乎 MSVC(再次)不支持它,但 GCC 和 CLang (see here) 支持它。
标签: c++ templates c++11 template-meta-programming default-arguments