【问题标题】:Is there a reason declval returns add_rvalue_reference instead of add_lvalue_reference是否有原因 declval 返回 add_rvalue_reference 而不是 add_lvalue_reference
【发布时间】:2013-11-30 17:20:13
【问题描述】:

将类型更改为reference 为一种类型,允许访问该类型的成员而无需创建该类型的实例。 lvalue referencesrvalue references 似乎都是如此。

declval 是用add_rvalue_reference 实现的而不是 add_lvalue_reference,

  • 这只是一个约定吗,
  • 或者是否有使用add_rvalue_reference 更可取的示例?

编辑: 我想我有点含糊,这些答案都很好,但触及的点略有不同。建议使用两种不同的答案,霍华德强调您可以选择您的类型具有的引用,使add_rvalue_reference 更加灵活。其他答案强调默认行为会自动选择更自然地反映输入类型的引用。不知道选什么!如果有人可以添加两个简单的示例,分别激发对每个属性的需求,那么我会很满意。

【问题讨论】:

  • “将类型更改为对类型的引用,允许访问该类型的成员而无需创建该类型的实例。”你在哪里读到的?

标签: c++ c++11


【解决方案1】:

add_rvalue_reference:

  • declval<Foo>() 的类型为 Foo&&
  • declval<Foo&>() 属于 Foo& 类型(引用折叠:“Foo& &&”折叠为 Foo&)。
  • declval<Foo&&>() 属于 Foo&& 类型(引用折叠:“Foo&& &&”折叠为 Foo&&)。

add_lvalue_reference:

  • declval<Foo>() 的类型为 Foo&
  • declval<Foo&>() 将是 Foo& 类型(引用折叠:“Foo& &”折叠为 Foo&)。
  • declval<Foo&&>() 的类型为 Foo& (!)(引用折叠:“Foo&& &”折叠为 Foo&)。

也就是说,你永远不会得到Foo&&

此外,declval&lt;Foo&gt;()Foo&amp;&amp; 类型的事实很好(你可以写 Foo&amp;&amp; rr = Foo(); 但不能写 Foo&amp; lr = Foo();)。 declval&lt;Foo<b>&amp;&amp;</b>&gt;() 将是 Foo<b>&amp;</b> 类型,只是感觉“错误”!


编辑:既然你问了一个例子:

#include <utility>
using namespace std;

struct A {};
struct B {};
struct C {};

class Foo {
public:
    Foo(int) { } // (not default-constructible)

    A onLvalue()   &  { return A{}; }
    B onRvalue()   && { return B{}; }
    C onWhatever()    { return C{}; }
};

decltype( declval<Foo& >().onLvalue()   ) a;
decltype( declval<Foo&&>().onRvalue()   ) b;
decltype( declval<Foo  >().onWhatever() ) c;

如果declval 使用了add_lvalue_reference,你就不能使用onRvalue()(第二个decltype)。

【讨论】:

  • Foo 是用户定义类型时,除了成员函数中的引用匹配之外,还有其他理由将declval&lt;Foo&gt;(或declval&lt;Foo&amp;&amp;&gt;)与declval&lt;Foo&amp;&gt; 区分开来吗?
  • 如果 declval() 作为 T 的右值引用返回,那么 declval( ) 不返回 Foo&&?为什么会调用 onWhatever()?
【解决方案2】:

是的,add_rvalue_reference 的使用让客户可以选择指定他想要给定类型的左值还是右值对象:

#include <type_traits>
#include <typeinfo>
#include <iostream>
#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <typename T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

int
main()
{
    std::cout << type_name<decltype(std::declval<int>())>() << '\n';
    std::cout << type_name<decltype(std::declval<int&>())>() << '\n';
}

对我来说输出:

int&&
int&

【讨论】:

  • 那个类型的名字生成器:)
【解决方案3】:

您希望能够取回 aTT&amp;const/volatile 合格版本。由于可能没有复制或移动构造函数,因此您不能只返回类型,即需要返回引用。另一方面,将右值引用添加到引用类型没有任何效果;

 std::declval<T>  -> T&&
 std::declval<T&> -> T&

也就是说,添加一个右值引用类型会产生一个看起来像传递类型的对象的结果!

【讨论】:

    【解决方案4】:

    当您需要提供noexcept 规范时,可以在我的df.operators 库中找到需要控制返回类型的示例。这是一个典型的方法:

    friend T operator+( const T& lhs, const U& rhs )
      noexcept( noexcept( T( lhs ),
                          std::declval< T& >() += rhs,
                          T( std::declval< T& >() ) ) )
    {
        T nrv( lhs );
        nrv += rhs;
        return nrv;
    }
    

    在通用代码中,您需要准确了解自己在做什么。在上面,TU 是我无法控制的类型,而noexcept 规范从 const 左值引用、非 const 左值引用和右值引用的副本可能不同。因此,我需要能够表达以下情况:

    • 我可以从T&amp; 构造T 吗? (使用T(std::declval&lt;T&amp;&gt;())
    • 我可以从const T&amp; 构造T 吗? (使用T(std::declval&lt;const T&amp;&gt;())
    • 我可以从T&amp;&amp; 构造T 吗? (使用T(std::declval&lt;T&gt;())

    幸运的是,std::declval 允许使用 std::add_rvalue_reference 并引用折叠规则。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-12
      • 2016-06-02
      • 1970-01-01
      • 1970-01-01
      • 2017-11-27
      • 2010-09-15
      • 1970-01-01
      • 2018-07-02
      相关资源
      最近更新 更多