typedef与c ++元编程中的公共继承 [英] typedef vs public inheritance in c++ meta-programming

查看:118
本文介绍了typedef与c ++元编程中的公共继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明:该问题与继承而不是typedef 完全不同目前无法找到任何类似的问题



我喜欢使用c ++模板元编程(在家里主要是,我有时在工作中轻轻地介绍它,我不想让程序变得只读的任何人谁没有打扰,了解它),但是我一直被编译器错误时,一旦出错了。



问题是,当然c ++模板元程序设计是基于模板的,因此任何时候你在一个深层嵌套的模板结构中得到一个编译器错误,你必须在一个10行的错误消息。我甚至习惯在文本编辑器中复制/粘贴消息,然后缩进消息以获得一些结构,直到我得到实际发生的事情的想法,这增加了一些工作来跟踪错误本身。



据我所知,问题主要是由于编译器以及如何输出typedefs(还有其他问题,例如嵌套的深度,但是这不是真正的编译器故障)。酷的功能,如可变模板或类型推导(自动)宣布即将到来的C ++ 0x,但我真的想要有更好的错误消息来启动。使用模板元程序可以证明是痛苦的,我想知道当更多的人实际进入它们时会变成什么。



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

  typedef partition< AnyType> MyArg; 

struct MyArg2:partition< AnyType> {};

这不是很多字符要输入,这在我看来不是不可读性。事实上,它甚至可以更可读,因为它保证了声明的新类型接近左边距,而不是在一个未确定的右边偏移。



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

 模板< class T>夯; get(partition< T>&); 

这种方式我确信它只能为一个合适的对象调用。



特别是当重载运算符如operator +时,你需要一些方法来缩小运算符的范围,或者冒着调用int的风险。



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



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

 模板< class Derived,class T&划分; 

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

这允许知道在编译器使用之前用于调用方法的'公共继承。应该注意,这减少了调用这个特定函数的机会,因为编译器必须执行转换,但是我从来没有注意到任何问题。



另一个解决方案这个问题是添加一个'tag'属性到我的类型,以区分彼此,然后依赖 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 * /};

//使用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
>
>
>
{
/ *有用的东西在这里* /
};

//或使用静态assert

模板< class T>
class MyClass
{
BOOST_STATIC_ASSERT((/ *这个标签的比较... * /));
};

我更倾向于使用'static assert''enable_if',我认为当我在一段时间后回来时更容易阅读。



好吧,基本上我还没有想过,我还在试验这里暴露的不同技术。 >

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



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



编辑:



我正在浏览stackoverflow,只是发现这个perl从Boost.MPL我完全忘记:



BOOST_MPL_ASSERT_MSG



提供宏3参数:




  • 检查

  • 的条件
  • 应用于显示错误消息

  • 所涉及的类型列表(作为元组)



解决方案

您想要做的是:显式检查作为模板参数传递的类型是否提供了必要的概念。缺少的概念功能,这是抛出了C + + 0X(因此是它成为C ++ 1X的主要罪魁祸首),它是肯定很难做适当的概念检查。自90年代以来,已经有几次尝试创建没有语言支持的概念检查库,但是,基本上,所有这些已经实现的是,为了做到正确,概念需要成为核心语言的特征,而不是比只有库的功能。



我没有找到你的想法而不是 typedef 并使用 enable_if 非常有吸引力。正如你自己说的,它经常掩盖实际代码只是为了更好的编译器错误消息。



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


Disclaimer: the question is completely different from Inheritance instead of typedef and I could not find any similar question so far

I like to play with c++ template meta-programming (at home mostly, I sometimes introduce it lightly at work but I don't want to the program to become only readable to anyone who did not bother learning about it), however I have been quite put out by the compiler errors whenever something goes wrong.

The problem is that of course c++ template meta-programming is based on template, and therefore anytime you get a compiler error within a deeply nested template structure, you've got to dig your way in a 10-lines error message. I have even taken the habit of copy/pasting the message in a text-editor and then indent the message to get some structure until I get an idea of what is actually happening, which adds some work to tracking the error itself.

As far as I know, the problem is mostly due to the compiler and how it output typedefs (there are other problems like the depth of nesting, but then it's not really the compiler fault). Cool features like variadic templates or type deduction (auto) are announced for the upcoming C++0x but I would really like to have better error messages to boot. It can prove painful to use template meta-programming, and I do wonder what this will become when more people actually get into them.

I have replaced some of the typedefs in my code, and use inheritance instead.

typedef partition<AnyType> MyArg;

struct MyArg2: partition<AnyType> {};

That's not much more characters to type, and this is not less readable in my opinion. In fact it might even be more readable, since it guarantees that the new type declared appears close to the left margin, instead of being at an undetermined offset to the right.

This however involves another problem. In order to make sure that I didn't do anything stupid, I often wrote my templates functions / classes like so:

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

This way I was sure that it can only be invoked for a suitable object.

Especially when overloading operators such as operator+ you need some way to narrow down the scope of your operators, or run the risk of it been invoked for int's for example.

However, if this works with a typedef'ed type, since it is only an alias. It sure does not work with inheritance...

For functions, one can simply use the CRTP

template <class Derived, class T> partition;

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

This allows to know the 'real' type that was used to invoke the method before the compiler used the public inheritance. One should note that this decrease the chances this particular function has to be invoked since the compiler has to perform a transformation, but I never noticed any problem so far.

Another solution to this problem is adding a 'tag' property to my types, to distinguish them from one another, and then count on 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&)
{
  ...
}

It requires some more typing though, especially if one declares and defines the function / method at different places (and if I don't bother my interface is pretty soon jumbled). However when it comes to classes, since no transformation of types is performed, it does get more complicated:

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...*/));
};

I tend to use more the 'static assert' that the 'enable_if', I think it is much more readable when I come back after some time.

Well, basically I have not made my mind yet and I am still experimenting between the different technics exposed here.

Do you use typedefs or inheritance ? How do you restrict the scope of your methods / functions or otherwise control the type of the arguments provided to them (and for classes) ?

And of course, I'd like more that personal preferences if possible. If there is a sound reason to use a particular technic, I'd rather know about it!

EDIT:

I was browsing stackoverflow and just found this perl from Boost.MPL I had completely forgotten:

BOOST_MPL_ASSERT_MSG

The idea is that you give the macro 3 arguments:

  • The condition to check
  • a message (C++ identifier) that should be used for display in the error message
  • the list of types involved (as a tuple)

It may help considerably in both code self documentation and better error output.

解决方案

What you are trying to do is to explicitly check whether types passed as template arguments provide the concepts necessary. Short of the concept feature, which was thrown out of C++0X (and thus being one of the main culprits for it becoming C++1X) it's certainly hard to do proper concept checking. Since the 90ies there have been several attempts to create concept-checking libraries without language support, but, basically, all these have achieved is to show that, in order to do it right, concepts need to become a feature of the core language, rather than a library-only feature.

I don't find your ideas of deriving instead of typedef and using enable_if very appealing. As you have said yourself, it often obscures the actual code only for the sake of better compiler error messages.

I find the static assert a lot better. It doesn't require changing the actual code, we all are used to having assertion checks in algorithms and learned to mentally skip over them if we want to understand the actual algorithms, it might produce better error messages, and it will carry over to C++1X better, which is going to have a static_assert (completely with class designer-provided error messages) built in into the language. (I suspect BOOST_STATIC_ASSERT to simply use the built-in static_assert if that's available.)

这篇关于typedef与c ++元编程中的公共继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆