【问题标题】:How to specialize a traits class for T as well as all of T's descendants如何为 T 以及 T 的所有后代专门化一个特征类
【发布时间】:2014-03-24 22:38:14
【问题描述】:

我想让一个特征类应用于一个类型以及它的后代。这可能吗?

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.

有什么办法可以代替说(借用和滥用 Java 表示法):

template <typename E>
struct wooble_traits<? extends Garble<E>> {
  typedef E elem_type
};

typedef typename wooble_traits<IntGarble>::elem_type IGType;
//Fine, IGType is an alias for int

注意:
尝试使 Dyp 的解决方案适应我的示例不起作用,因为 Garble 采用类型参数。似乎没有任何地方可以推断该参数。

#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;
};

在 gcc-4.6 中,这会产生:

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

这是可以理解的,因为 GCC 无法知道 E 的值。

【问题讨论】:

  • @Jefffrey:例如IntGarble。子类/派生类。
  • 我的后代是指派生类。特征类使有关类型的信息在编译时可用。 wooble_traits 公开 wooble 的 elem_type 以在编译时使用。当被视为 wooble 时,Garble 的 elem_type 与其模板参数相同。来自某些 Garble(包括 IntGarble)的所有派生类都具有与其 Garble 祖先相同的 elem_type(在 IntGarble 的情况下,此类型为 int)。

标签: c++ templates inheritance template-specialization typetraits


【解决方案1】:

对于基础

#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";
}

由于某种原因,当表达式依赖于(以前的)类型参数时,不允许专门处理非类型模板参数。

g++4.8.2 无法编译这个 btw (ICE),但它 works fine with clang++3.5


这是在两个编译器上编译的替代版本:

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
{};

enable_if 实际上并不是必需的。 std::conditional 也可以,但 enable_if 在这里更短。


如果基础是模板特化,我们可以使用Jarod42's solution(我稍微修改了一下):

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
{};

注意:这只适用于公共继承(而且我认为它还有一些进一步的限制:虚拟和某些多重继承的情况也不应该起作用)。


这是“C++03 + boost”风格的相同类型特征的一个版本:

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));
};

【讨论】:

  • 好的,但是当 foo 采用模板参数时怎么样(如我的示例所示)。那你怎么做? (不强制客户端知道该参数的类型)
  • @dspyz 哎呀,对不起,我把测试用例简化得太多了。添加了基类是模板特化的解决方案。
  • 好吧,这看起来不错,但我正在处理的项目不使用 C++11(正如您可能从我基于 boost 的示例中猜到的那样)。有没有什么方法可以在没有 decltype 的情况下做到这一点?
  • 哦,boost 有一个 TYPEOF 宏,看起来它做同样的事情。我会测试它是否有效。
  • @dspyz decltype 部分只是让 SFINAE 更方便一些。我会尝试用老式的 SFINAE 替换它。
猜你喜欢
  • 2014-08-13
  • 2019-08-11
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 2018-06-17
  • 2010-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多