【问题标题】:typedef vs public inheritance in c++ meta-programmingc++元编程中的typedef与公共继承
【发布时间】:2009-10-04 11:52:30
【问题描述】:

免责声明:该问题与Inheritance instead of typedef 完全不同,到目前为止我找不到任何类似的问题

我喜欢玩 c++ 模板元编程(主要是在家里,我有时会在工作中简单地介绍它,但我不希望程序变得只有不费心学习它的人才能阅读),但是每当出现问题时,我都会被编译器错误所困扰。

问题在于,c++ 模板元编程当然是基于模板的,因此,每当您在深度嵌套的模板结构中遇到编译器错误时,您都必须在 10 行错误消息中挖掘自己的方式。我什至习惯于在文本编辑器中复制/粘贴消息,然后缩进消息以获得某种结构,直到我了解实际发生的情况,这为跟踪错误本身增加了一些工作。

据我所知,问题主要在于编译器及其输出 typedef 的方式(还有其他问题,例如嵌套深度,但实际上并不是编译器的错误)。即将推出的 C++0x 发布了诸如可变参数模板或类型推导(自动)之类的酷功能,但我真的希望有更好的错误消息来引导。使用模板元编程可能会很痛苦,我确实想知道当更多人真正参与其中时会变成什么。

我已经替换了我代码中的一些 typedef,并改用继承。

typedef partition<AnyType> MyArg;

struct MyArg2: partition<AnyType> {};

要输入的字符不多,而且在我看来,它的可读性并不差。事实上,它甚至可能更具可读性,因为它保证声明的新类型显示在左边距附近,而不是在右侧未确定的偏移处。

然而,这涉及到另一个问题。为了确保我没有做任何愚蠢的事情,我经常这样编写模板函数/类:

template <class T> T& get(partition<T>&);

这样我确信它只能被一个合适的对象调用。

尤其是在重载运算符(例如 operator+)时,您需要某种方法来缩小运算符的范围,或者冒着被 int 调用的风险。

但是,如果这适用于 typedef 的类型,因为它只是一个别名。它肯定不适用于继承...

对于函数,可以简单地使用CRTP

template <class Derived, class T> partition;

template <class Derived, class T> T& get(partition<Derived,T>&);

这允许在编译器使用公共继承之前知道用于调用方法的“真实”类型。应该注意的是,由于编译器必须执行转换,这减少了必须调用此特定函数的机会,但到目前为止我从未注意到任何问题。

解决此问题的另一种方法是在我的类型中添加一个“标签”属性,以将它们彼此区分开来,然后依靠SFINAE

struct partition_tag {};

template <class T> struct partition { typedef partition_tag tag; ... };

template <class T>
typename boost::enable_if<
  boost::same_type<
    typename T::tag,
    partition_tag
  >,
  T&
>::type
get(T&)
{
  ...
}

但它需要更多的输入,尤其是如果在不同的地方声明和定义函数/方法(如果我不打扰我的界面很快就会混乱)。然而,当涉及到类时,由于没有执行类型转换,它确实变得更加复杂:

template <class T>
class MyClass { /* stuff */ };

// Use of boost::enable_if

template <class T, class Enable = void>
class MyClass { /* empty */ };

template <class T>
class MyClass <
  T,
  boost::enable_if<
    boost::same_type<
      typename T::tag,
      partition_tag
    >
  >
>
{
  /* useful stuff here */
};

// OR use of the static assert

template <class T>
class MyClass
{
  BOOST_STATIC_ASSERT((/*this comparison of tags...*/));
};

我倾向于使用更多的“静态断言”而不是“enable_if”,我认为一段时间后回来时它更具可读性。

好吧,基本上我还没有下定决心,我仍在尝试这里公开的不同技术。

你使用 typedef 还是继承? 您如何限制方法/函数的范围或以其他方式控制提供给它们(和类)的参数类型?

当然,如果可能的话,我想要更多的个人喜好。如果有充分的理由使用特定技术,我宁愿知道!

编辑:

我正在浏览 stackoverflow,刚刚从 Boost.MPL 中找到了这个我完全忘记的 perl:

BOOST_MPL_ASSERT_MSG

这个想法是你给宏 3 个参数:

  • 要检查的条件
  • 应用于在错误消息中显示的消息(C++ 标识符)
  • 涉及的类型列表(作为元组)

它可能对代码自文档和更好的错误输出都有很大帮助。

【问题讨论】:

  • Re: 为什么会有两个标签?因为有人输入得足够快,并且不在乎检查旧标签。然后有两个。人们选择了他们认为拼写正确的任何一个。据我所知,一旦在 SO 上创建标签,即使它被清空,它仍然会显示在可用标签列表中。
  • meta-programming”有 33 个条目,而“metaprogramming”有 134 个条目。我会发现前者更好,但如果大多数... 无论如何,关于解决 33 个旧问题并重新标记它们的政策是什么?
  • sbi:我继续将它们全部重新标记为“元编程”
  • @kitchen:你很好,但遗憾的是我是对的,旧的meta-programming 标签仍然显示在创建新问题页面上。哦,好吧。
  • Matthew:标签更新可能需要一些时间?希望它会在一周内消失。

标签: c++ metaprogramming


【解决方案1】:

您要做的是明确检查作为模板参数传递的类型是否提供了必要的概念。缺少从 C++0X 中抛弃的概念功能(因此成为它成为 C++1X 的主要罪魁祸首之一),当然很难进行正确的概念检查。自 90 年代以来,已经有几次尝试在没有语言支持的情况下创建概念检查库,但基本上,所有这些都表明,为了正确地做到这一点,概念需要成为核心语言的一个特征,而不是而不是仅限库的功能。

我不觉得你的派生想法而不是typedef 和使用enable_if 非常吸引人。正如您自己所说,它通常只是为了更好的编译器错误消息而掩盖实际代码。

我发现静态断言要好得多。它不需要更改实际代码,我们都习惯于在算法中进行断言检查,并且如果我们想了解实际算法,就学会了在心理上跳过它们,它可能会产生更好的错误消息,并且会延续到 C ++1X 更好,它将在语言中内置 static_assert(完全带有类设计器提供的错误消息)。 (我怀疑BOOST_STATIC_ASSERT 只是使用内置的static_assert 如果可用的话。)

【讨论】:

  • @Matthieu:我不太同意这种说法:毕竟,为了获得更好的运行时错误消息,你确实会弄乱你的运行时代码。但似乎static_assert 实现了大致相同的效果,但杂乱程度更低。
  • 是的,这就是我的意思......好吧,我确实有一些英语问题:\ ...我只是说使用 enable_if 是一种非常冗长的测试方式(与 static_assert 相比),我确实认为在某些情况下,这是唯一可能的方法,否则为什么要让 Boost 家伙创造这样的野兽?
  • 我不认为 enable_if 是为了帮助调试 TMP 代码而创建的。
猜你喜欢
  • 1970-01-01
  • 2016-07-10
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
  • 2011-03-11
  • 2011-08-10
相关资源
最近更新 更多