【问题标题】:std::remove_reference explained?std::remove_reference 解释了吗?
【发布时间】:2013-06-04 17:02:01
【问题描述】:

我看到std::remove_reference 的可能实现如下

template< class T > struct remove_reference      {typedef T type;};
template< class T > struct remove_reference<T&>  {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};   

为什么lvaluervalue reference 有专门化?通用模板本身是否足够并删除参考?我在这里很困惑,因为在T&amp;T&amp;&amp; 专业化中,如果我尝试使用::type,我仍然应该分别得到T&amp;T&amp;&amp;,对吗?

您能解释一下为什么我们在 move 中投射到remove_reference&lt;t&gt;::type&amp;&amp; 吗? (是不是因为参数被命名了,所以在move函数中会被当作左值处理?)。

另外,您能否指出一种方法,让我可以找出并打印类型是什么?例如,如果它是int 类型的rvalue,那么我应该能够打印出int&amp;&amp; 已通过? (我一直在使用std::is_same 手动检查。)

感谢您的宝贵时间。

【问题讨论】:

  • 这是一个非常的好问题!

标签: c++ templates c++11 template-specialization


【解决方案1】:

为什么会有左值和右值引用的特化?

如果只有主模板存在,那么做:

remove_reference<int&>::type

会给你:

int&

然后做:

remove_reference<int&&>::type

会给你:

int&&

这不是你想要的。左值引用和右值引用的特化允许分别从您传递的类型参数中剥离 &amp;&amp;&amp;

例如,如果你正在做:

remove_reference<int&&>

int&amp;&amp; 类型将匹配 T&amp;&amp; 特化指定的模式,Tint。由于特化将类型别名 type 定义为 T(在本例中为 int),因此:

remove_reference<int&&>::type

会给你int

您能否解释一下,为什么我们在move 中转换为remove_reference&lt;t&gt;::type&amp;&amp;

这是因为如果move() 定义如下:

    template<typename T>
    T&& move(T&& t) { ... }
//  ^^^
//  Resolves to X& if T is X& (which is the case if the input has type X
//  and is an lvalue)

如果move() 的参数是X 类型的左值,则返回类型将为X&amp;(这就是所谓的“通用引用”)。我们要确保返回类型是 always 一个右值引用。

move() 的目的是为您返回一个右值,无论您输入什么。由于对返回类型为右值引用的函数的函数调用是一个右值,我们真的希望move() 始终返回一个右值引用。

这就是我们使用remove_reference&lt;T&gt;::type&amp;&amp; 的原因,因为将&amp;&amp; 附加到非引用类型总是保证产生右值引用类型。

您能否指出一种方法,让我可以找出并打印类型是什么?

我不确定您在这里所说的“打印”是什么意思。我知道没有可移植的方式将类型的名称转换为字符串(无论您如何获得该类型)。

另一方面,如果您的目标是确保传递了一个右值,则可以使用如下静态断言:

#include <type_traits>

template<typename T>
void foo(T&&)
{
    static_assert(!std::is_reference<T>::value, "Error: lvalue was passed!");
    // ...
}

这依赖于这样一个事实:当传递X 类型的左值时,T 将被推导出为X&amp;

如果您只想产生替换失败,也可以使用等效的 SFINAE 约束:

#include <type_traits>

template<typename T, typename std::enable_if<
    !std::is_reference<T>::value>::type* = nullptr>
void foo(T&&)
{
    // ...
}

【讨论】:

  • 那为什么是T&amp;&amp;的专业化?通用模板可以处理这个吗?
  • @Koushik:你是说主模板?好吧,如果只有主模板存在,那么remove_reference&lt;int&amp;&amp;&gt;::type 会还给你int&amp;&amp;
  • 是的,进行了更正。在专业中我们也得到同样的权利吗?对于T&amp;&amp;,对于::type,我们得到T&amp;&amp;,对吗?
  • @Koushik: 不,我们得到T,因为特化匹配模式T&amp;&amp;,所以如果你通过(比如)int&amp;&amp; 并匹配T&amp;&amp;,你的@987654363 @ 必须是 int
  • 哦好吧好吧好吧...:-) 我太笨了。现在一切都清楚了。我印象深刻的是,对于T&amp;&amp; 专业化,typedef 中的T 仍然是T&amp;&amp;。但我错了。它是 T 。非常感谢:-)
【解决方案2】:

当您将某些类型视为模板参数时,编译器会搜索最“专业化”的专业化。如果您将 int&& 传递给此模板,编译器将使用 remove_reference&lt;T&amp;&amp;&gt; 版本。一般专业不会给您想要的 - 如果您将 int&amp;&amp; 传递给一般专业,则类型将为 int&amp;&amp;

如果要打印类型,请使用typeid(some_type).name()

【讨论】:

  • 我使用了 typeid 但在 gcc 中有点尴尬。我想我必须使用一些过滤器对吗?除了 typeid 之外还有什么编译时间的东西吗?这样我就可以与 static_assert 一起使用?
  • @Koushik:您能否澄清一下问题的那一部分?你想得到一个包含类型名称的字符串,还是只想确保传递了一个右值引用?
  • @AndyProwl 在调试阶段获取字符串会很好,是的,我想确保只有右值被传递。如果我能在编译时得到这个,那么它最有帮助。
  • @Koushik:好的,我编辑了我的答案。但是,没有可移植的方法来获取类型的名称(在某种程度上这是可能的:您可以在谷歌上搜索“C++ 漂亮打印”)
  • 您可以运行 c++filt -t 来解开 gcc 的 typeid()::name() 输出。例如,c++filt -t FRSt8ios_baseS0_E 返回 std::ios_base&amp; (std::ios_base&amp;) - 这是函数 std::boolalpha 的类型。
猜你喜欢
  • 2020-09-15
  • 2013-06-22
  • 1970-01-01
  • 2020-12-23
  • 2023-04-07
  • 2015-12-22
  • 2016-02-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多