libc ++ vs libstdc ++ std :: is_move_assignable:哪个最正确?

时间:2016-10-11 20:29:35

标签: c++ c++11 c++14 libstdc++ libc++

我试图通过阅读C ++ 14标准以及libc ++和libstdc ++的源代码来更深入地理解C ++。各种type_traits项目的实施在两者之间有所不同,尤其是is_move_assignable,我试图弄清楚哪些项目“更正确”。

的libc ++:

template <class _Tp> struct is_move_assignable
    : public is_assignable<typename add_lvalue_reference<_Tp>::type,
                           const typename add_rvalue_reference<_Tp>::type> {};

的libstdc ++:

template<typename _Tp, bool = __is_referenceable<_Tp>::value>
  struct __is_move_assignable_impl;

template<typename _Tp>
  struct __is_move_assignable_impl<_Tp, false>
  : public false_type { };

template<typename _Tp>
  struct __is_move_assignable_impl<_Tp, true>
  : public is_assignable<_Tp&, _Tp&&>
  { };

template<typename _Tp>
  struct is_move_assignable
  : public __is_move_assignable_impl<_Tp>
  { };

标准规定:

  

对于可引用类型T,结果与is_assignable<T&, T&&>::value相同,否则为false

我注意到的第一件事是libc ++将const应用于第二个模板参数,这似乎不正确,因为移动赋值运算符采用非const rvalue。 libstdc ++也使用__is_referenceable,它遵循标准的措辞,但libc ++没有。 libc ++使用add_lvalue_referenceadd_rvalue_reference是否涵盖了这个要求,它们都自己强制执行__is_referenceable

我非常感谢每个项目选择他们的解决方案的原因!

3 个答案:

答案 0 :(得分:8)

  

谢谢!知道作者为什么可能添加了const,然后呢?

我最好的猜测是暂时的(希望)精神错乱:

https://github.com/llvm-mirror/libcxx/commit/6063ec176d5056683d6ddd310c2e3a8f1c7e1b46#diff-48f5ee43879b5ad38888f0a6ead10113R1245

- )

我删除了const并运行了当前的单元测试,没有任何失败。

答案 1 :(得分:6)

对于任何可引用的东西,这两个实现都做同样的事情,因为libc ++中无关的is_copy_assignable没有意义但也无害。

(从差异来看,它对我来说当然看起来像是暂时的疯狂:)似乎是void的(错误)实现的C&amp; P问题。)

对于任何不可引用的东西(即cv false_type或可恶的函数类型),libstdc ++返回add_{l,r}value_reference

在libc ++中,const将其保持不变(这取决于后期C ++ 14的问题解决方案)。在顶部放置const不会对AFT产生任何影响,并为void y类型添加is_assignable

然后我们转到declval<T>() = declval<U>(),其中SFINAE测试T == U == some AFTT == some void typeU = some const-qualified void type的格式false_type。在所有情况下,表达形式都不正确(以SFINAE友好的方式),因此我们得到#include <ncurses.h> int main (int argc, char *argv[]) { int ch, c; initscr (); cbreak (); noecho (); while (ch != 'q') { switch (c = getch ()) { case 'a': ch = 'b'; break; case 'b': ch = 'c'; break; case 'c': ch = 'd'; break; default: ch = c; break; } printw ("%c", ch); } endwin (); return (0); }

这两者是等价的。

答案 2 :(得分:5)

__is_referenceable是一个非标准的内部libstdc ++例程。 (这并不意味着它很糟糕,只是因为我不希望libc ++使用它)。此外,&#34;是可引用的&#34;概念出现的时间远远晚于is_move_assignable

__is_referenceable有助于处理&#34;恶劣的功能&#34 ;;像int (*) (double) &&这样的东西。

看起来我需要编写更多测试: - )