c++ - How to specialize a traits class for T as well as all of T's descendants -
i want make traits class apply type descedants. possible?
template <typename e> struct garble { }; template <typename t> struct wooble_traits; template <typename e> struct wooble_traits<garble<e>> { typedef e elem_type; }; struct intgarble : public garble<int> { }; typedef typename wooble_traits<intgarble>::elem_type igtype; //error, wooble_traits<intgarble> has no definition.
is there way instead (borrowing , abusing java notation):
template <typename e> struct wooble_traits<? extends garble<e>> { typedef e elem_type }; typedef typename wooble_traits<intgarble>::elem_type igtype; //fine, igtype alias int
note:
attempting adapt dyp's solution example doesn't work since garble takes type parameter. there doesn't seem anywhere infer parameter.
#include <boost/type_traits/is_base_of.hpp> template <typename t, typename c = void> struct wooble_traits; template <typename t, typename e> struct wooble_traits<t, typename boost::is_base_of<garble<e>, t>::type> { typedef e elem_type; };
in gcc-4.6 produces:
g++ -i/usr/include/boost/utility -i/usr/include/boost/type_traits -o0 -g3 -wall -c -fmessage-length=0 -mmd -mp -mf"main.d" -mt"main.d" -o "main.o" "../main.cpp" ../main.cpp:15:8: error: template parameters not used in partial specialization: ../main.cpp:15:8: error: ‘e’ make: *** [main.o] error 1
which understandable since there's no way gcc know value of e.
for base classes:
#include <type_traits> template<bool b> using stdbool_t = std::integral_constant<bool, b>; template<class t, class u = std::true_type> struct trait : std::false_type {}; struct foo {}; struct bar : foo {}; template<class t> struct trait<t, stdbool_t<std::is_base_of<foo, t>{}>> : std::true_type {}; #include <iostream> int main() { std::cout << std::boolalpha; std::cout << trait<int>::value << "\n"; std::cout << trait<foo>::value << "\n"; std::cout << trait<bar>::value << "\n"; }
for reason, specializing on non-type template parameters isn't allowed when expression depends on (the previous) type parameters.
g++4.8.2 fails compile btw (ice), works fine clang++3.5
here's alternative version compiles on both compilers:
template<class t, class = void> struct trait : std::false_type {}; struct foo {}; struct bar : foo {}; template<class t> struct trait<t, typename std::enable_if<std::is_base_of<foo, t>{}>::type> : std::true_type {};
the enable_if
isn't necessary, actually. std::conditional
work well, enable_if
shorter here.
if base template specialization, can use jarod42's solution (i've modified bit):
template<template<class...> class t, class u> struct is_base_template_of { private: template<class... v> static auto test(const t<v...>&) -> decltype(static_cast<const t<v...>&>(std::declval<u>()), std::true_type{}); static std::false_type test(...); public: static constexpr bool value = decltype(is_base_template_of::test(std::declval<u>()))::value; }; template<class t> struct trait<t, typename std::enable_if<is_base_template_of<foo, t>::value>::type> : std::true_type {};
note: works public inheritance (and think has further restrictions: virtual , cases of multiple inheritance shouldn't work either).
here's version of same type trait in "c++03 + boost" style:
template<template<class> class t, class u> struct is_base_template_of { private: typedef char false_type; typedef char(& true_type)[2]; template<class v> static true_type test(const t<v>*); static false_type test(...); public: static const bool value = ( sizeof(test(std::declval<typename std::remove_reference<u>::type*>())) == sizeof(true_type)); };
Comments
Post a Comment