免责声明:这个问题与继承而不是 typedef https://stackoverflow.com/questions/441744/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>&);
这样我就确信只能为合适的对象调用它。
特别是当重载运算符(例如运算符+)时,您需要某种方法来缩小运算符的范围,或者冒着被调用 int 等操作的风险。
但是,如果这适用于 typedef'ed 类型,因为它只是一个别名。它肯定不适用于继承......
对于函数,可以简单地使用CRTP http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
template <class Derived, class T> partition;
template <class Derived, class T> T& get(partition<Derived,T>&);
这允许在编译器使用公共继承之前知道用于调用该方法的“真实”类型。应该注意的是,这减少了必须调用该特定函数的机会,因为编译器必须执行转换,但到目前为止我从未注意到任何问题。
此问题的另一种解决方案是向我的类型添加“标签”属性,以区分它们,然后依靠SFINAE http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error.
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 还是继承?
如何限制方法/函数的范围或以其他方式控制提供给它们(以及类)的参数类型?
当然,如果可能的话,我想要更多个人喜好。如果有充分的理由使用某种特定技术,我宁愿知道它!
EDIT:
我正在浏览 stackoverflow,刚刚从 Boost.MPL 中找到了这个 perl,我完全忘记了:
BOOST_MPL_ASSERT_MSG http://www.boost.org/doc/libs/1_39_0/libs/mpl/doc/refmanual/assert-msg.html
这个想法是你给宏 3 个参数:
- 要检查的条件
- 应用于在错误消息中显示的消息(C++ 标识符)
- 涉及的类型列表(作为元组)
它可能对代码自我文档和更好的错误输出有很大帮助。