【问题标题】:Remove reference with const references使用 const 引用删除引用
【发布时间】:2013-01-25 13:11:17
【问题描述】:

对于参数类 C,我希望始终获得“原始”类型,而与指针、常量或引用修饰符无关。

template<typename __T>
class C
{
public:
    typedef std::some_magic_remove_all<__T>::type T;
}

int main()
{
    C<some_type>::type a;
}

例如,some_type 等于:

  • int&amp;
  • int**
  • int*&amp;
  • int const &amp;&amp;
  • int const * const
  • 等等

我希望 a 始终是 int 类型。我怎样才能实现它?

【问题讨论】:

  • 看看标准库是如何实现remove_reference、remove_pointer等(部分特化)的,简单地对所有你想剥离的东西做同样的事情,确保它是递归的(remove_all_extents应该是一个递归的例子)。
  • @vonbrand 因为我想强制 C 总是用普通类型实例化,没有修饰符,然后我的类使用指向这个“普通类型”的指针。其他方法是使用条件或 static_assert,但也许删除所有修饰符并添加文本前置条件是一种更舒适的方法。
  • int const &amp; const 无效。 :)

标签: c++ templates c++11 std


【解决方案1】:

如果你想更多地使用标准库,你可以这样做:

#include <type_traits>
template<class T, class U=
  typename std::remove_cv<
  typename std::remove_pointer<
  typename std::remove_reference<
  typename std::remove_extent<
  T
  >::type
  >::type
  >::type
  >::type
  > struct remove_all : remove_all<U> {};
template<class T> struct remove_all<T, T> { typedef T type; };

它会删除东西,直到不再改变类型。使用更新的标准,这可以缩短为

template<class T, class U=
  std::remove_cvref_t<
  std::remove_pointer_t<
  std::remove_extent_t<
  T >>>>
  struct remove_all : remove_all<U> {};
template<class T> struct remove_all<T, T> { typedef T type; };
template<class T> using remove_all_t = typename remove_all<T>::type;

【讨论】:

    【解决方案2】:
    template<class T> struct remove_all { typedef T type; };
    template<class T> struct remove_all<T*> : remove_all<T> {};
    template<class T> struct remove_all<T&> : remove_all<T> {};
    template<class T> struct remove_all<T&&> : remove_all<T> {};
    template<class T> struct remove_all<T const> : remove_all<T> {};
    template<class T> struct remove_all<T volatile> : remove_all<T> {};
    template<class T> struct remove_all<T const volatile> : remove_all<T> {};
    //template<class T> struct remove_all<T[]> : remove_all<T> {};
    //template<class T, int n> struct remove_all<T[n]> : remove_all<T> {};
    

    我最初也剥离了范围(数组),但 Johannes 注意到这会导致 const char[] 的歧义,并且问题没有提及它们。如果我们还想剥离数组(另见 cmets 中提到的想法),以下内容不会使事情变得太复杂:

    #include <type_traits>
    template<class U, class T = typename std::remove_cv<U>::type>
    struct remove_all { typedef T type; };
    template<class U, class T> struct remove_all<U,T*> : remove_all<T> {};
    template<class U, class T> struct remove_all<U,T&> : remove_all<T> {};
    template<class U, class T> struct remove_all<U,T&&> : remove_all<T> {};
    template<class U, class T> struct remove_all<U,T[]> : remove_all<T> {};
    template<class U, class T, int n> struct remove_all<U,T[n]> : remove_all<T> {};
    

    或使用辅助类但只有一个模板参数:

    #include <type_traits>
    template<class T> struct remove_all_impl { typedef T type; };
    template<class T> using remove_all =
      remove_all_impl<typename std::remove_cv<T>::type>;
    template<class T> struct remove_all_impl<T*> : remove_all<T> {};
    template<class T> struct remove_all_impl<T&> : remove_all<T> {};
    template<class T> struct remove_all_impl<T&&> : remove_all<T> {};
    template<class T> struct remove_all_impl<T[]> : remove_all<T> {};
    template<class T, int n> struct remove_all_impl<T[n]> : remove_all<T> {};
    

    如果所有变体开始看起来都差不多,这是正常的;-)

    【讨论】:

    • 是否需要T const volatile这一行?递归在这里也应该可以正常工作。
    • 尝试删除它...(是的,它是,否则它对于既是 const 又是 volatile 的类型是不明确的)
    • @AndyT 是的,我完全按照要求回答了这个问题,但我同意 vonbrand 的观点,即需要做这样的事情是可疑的。
    • 坏消息。 char const[] 匹配 &lt;T[]&gt;&lt;T const&gt;
    • 啊,谢谢你的警告,我错过了这个问题。编写 const、volatile 和 [] 或 [n] 的所有 11 种组合看起来很痛苦。我可以使用相互递归的模板,其中一个剥离 const 而另一个剥离 [] 来避免这种情况,但这又不是很好。
    【解决方案3】:

    你也可以使用remove_cvref_t函数,从c++20开始就可以使用了

    #include <iostream>
    #include <type_traits>
    
    
    int main()
    {
        std::cout << std::boolalpha
                  << std::is_same_v<std::remove_cvref_t<int>, int> << '\n'
                  << std::is_same_v<std::remove_cvref_t<int&>, int> << '\n'
                  << std::is_same_v<std::remove_cvref_t<int&&>, int> << '\n'
                  << std::is_same_v<std::remove_cvref_t<const int&>, int> << '\n'
                  << std::is_same_v<std::remove_cvref_t<const int[2]>, int[2]> << '\n'
                  << std::is_same_v<std::remove_cvref_t<const int(&)[2]>, int[2]> << '\n'
                  << std::is_same_v<std::remove_cvref_t<int(int)>, int(int)> << '\n';
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-23
      • 2010-12-19
      • 2013-12-20
      • 1970-01-01
      • 2011-05-14
      • 2011-10-12
      • 2019-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多