【问题标题】:Help with type traits帮助类型特征
【发布时间】:2010-01-14 07:59:11
【问题描述】:

假设我们有以下模板类

template<typename T> class Wrap { /* ... */ };

我们无法改变Wrap。这很重要。

假设有从Wrap&lt;T&gt; 派生的类。例如,

class NewInt  : public Wrap<int>     { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };
class Foo     : public Wrap<Bar>     { /* ... */ };

我们也无法更改这些课程。以上所有课程都是第 3 方。他们不是我的。

我需要以下编译时间type_traits

template<class T>
struct is_derived_from_Wrap { 
     static const bool value = /* */;
};

我需要什么?

assert(is_derived_from_Wrap<Int>::value == true);  // Indeed I need static assert
assert(is_derived_from_Wrap<MyClass>::value == true);
assert(is_derived_from_Wrap<char>::value == false);
struct X {};
assert(is_derived_from_Wrap<X>::value == false);

【问题讨论】:

  • 但是你能改变IntMyClass吗? :p
  • 为你的类型特征命名不是更好吗:has_Wrap_for_base?实际上,MyClass 并不是 Wrap 的基础。
  • 我很困惑。是int 还是Int?还有myClass 还是MyClass?还是这些不同?
  • @dtrosset:是的。你说的对。我的英语不好。
  • @sbi: 不同的类。

标签: c++ templates specialization typetraits compile-time-constant


【解决方案1】:

您可以使用 SFINAE 执行此操作,但如果您不知道发生了什么,它会有点神奇...

template<typename T> class Wrap { };

struct myclass {};
struct X {};

class Int     : public Wrap<int>     { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };

template< typename X >
struct is_derived_from_Wrap
{
  struct true_type { char _[1]; };
  struct false_type { char _[2]; };

  template< typename U >
    static true_type test_sfinae( Wrap<U> * w);
  static false_type test_sfinae( ... );

  enum { value = sizeof( test_sfinae( (X*)(0) ) )==sizeof(true_type) };
};


#include <iostream>
#define test(X,Y) std::cout<<( #X " == " #Y )<<"  : "<<( (X)?"true":"false") <<std::endl;

int main()
{
  test(is_derived_from_Wrap <Int>::value, true);
  test(is_derived_from_Wrap <MyClass>::value, true);
  test(is_derived_from_Wrap <char>::value, false);
  test(is_derived_from_Wrap <X>::value, false);
}

这给出了预期的输出

is_derived_from_Wrap <Int>::value == true  : true
is_derived_from_Wrap <MyClass>::value == true  : true
is_derived_from_Wrap <char>::value == false  : false
is_derived_from_Wrap <X>::value == false  : false

我的代码有几个问题。如果类型是 Wrap,它也会返回 true。

assert(  is_derived_from_Wrap< Wrap<char> >::value == 1 );

如果需要,可以使用更多的 SFINAE 魔法来解决此问题。

如果派生不是公共派生(即私有或受保护),它将返回 false

struct Evil : private Wrap<T> { };
assert( is_derived_from_Wrap<Evil>::value == 0 );

我怀疑这无法解决。 (但我可能错了)。但我怀疑公共继承就足够了。

【讨论】:

  • 我想我的代码中的简单部分特化不起作用?
  • 正确,因为只有类型完全匹配,专业化才会匹配。你的 is_Wrap >::value = true,但是 is_Wrap::value = false。
  • 太棒了。我以前认为test_sfinae( Wrap&lt;U&gt; * w);不能推导出U对于X *的情况。
【解决方案2】:

在一般情况下,您需要进行一些相当复杂的模板元编程来确定一个类 X 是否派生自另一个 Y。基本上 X 派生自 Y 如果:

  1. Y 可以隐式转换为 X
  2. X 和 Y 不是同一类型

Andrei Alexandrescu 在他的《现代 C++ 设计》一书中解释了如何做到这一点(以及许多其他模板技巧)。

您可以在 Loki 库或 uSTL 实现中找到解决问题的代码,两者均由 Alexandrescu 编写。

【讨论】:

  • 重新阅读问题后,我意识到我的建议并不能解决问题。我们不想知道 X 是否从 Y 派生,而是 X 是否从 Y 派生,其中 T 可以是任何类型。
  • 是的。你的答案不是解决方案。但你已经意识到问题所在了。
  • 你也可以引用 boost::is_base_of (它没有解决 OP 的问题,因为 Base 参数不能是类模板)。
【解决方案3】:

以下内容确定某物是否为 包裹:

template<class T>
struct is_Wrap { static const bool value = false; };

template<typename T> 
struct is_Wrap< Wrap<T> > { static const bool value = true; };

由于派生是一种 Is-A 关系,因此从 Wrap&lt;T&gt; 派生的所有内容也是 Wrap&lt;T&gt; 并且应该由此找到。

【讨论】:

  • 我的名字base of Wrap 打错了。我的英语不好。 Derived from wrap,当然。
  • 这不是很好的解决方案。然后我需要为每个班级定义。我不认识他们所有人。我想识别 X 是否派生自 Wrap 其中 Y 可以是任何类型。
  • @Alexey:我会尝试相应地调整我的代码。但你肯定需要更准确地提出问题。 (这不仅仅是语言障碍问题。)
  • 这仅测试类型是否恰好是 Wrap,而不是派生自。
  • @迈克尔;但是任何东西不是来自Wrap&lt;T&gt; 也是Wrap&lt;T&gt;吗?
猜你喜欢
  • 2013-05-28
  • 2011-11-11
  • 2014-06-10
  • 1970-01-01
  • 2019-09-05
  • 2016-07-31
  • 2015-03-30
  • 2010-12-03
  • 1970-01-01
相关资源
最近更新 更多