C++ declval返回add\u-rvalue\u引用而不是add\u-lvalue\u引用是否有原因

C++ declval返回add\u-rvalue\u引用而不是add\u-lvalue\u引用是否有原因,c++,c++11,C++,C++11,将某个类型更改为某个类型的引用,可以在不创建该类型实例的情况下访问该类型的成员。这似乎适用于左值引用和右值引用 declval是用add\u-rvalue\u-reference而不是add\u-lvalue\u-reference实现的 这只是一个惯例吗 或者是否有add\r\u reference更可取的使用示例 编辑: 我想我有点含糊其辞,这些答案都很好,但有点不同。建议使用两种不同的答案,Howard强调,您可以选择您的类型具有的引用,从而使add\u rvalue\u refere

将某个类型更改为某个类型的
引用
,可以在不创建该类型实例的情况下访问该类型的成员。这似乎适用于
左值引用
右值引用

declval
是用
add\u-rvalue\u-reference
而不是
add\u-lvalue\u-reference
实现的

  • 这只是一个惯例吗
  • 或者是否有
    add\r\u reference
    更可取的使用示例
编辑:
我想我有点含糊其辞,这些答案都很好,但有点不同。建议使用两种不同的答案,Howard强调,您可以选择您的类型具有的引用,从而使
add\u rvalue\u reference
更加灵活。其他答案强调默认行为会自动选择更自然地反映输入类型的引用。我不知道该选什么!如果有人能添加两个简单的示例,分别激发对每个属性的需求,那么我就满足了。

是的,使用
add\u rvalue\u 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';
}

使用
添加右值参考

  • declval()
    属于
    Foo&
    类型
  • declval()
    的类型为
    Foo&
    (参考折叠:“
    Foo&&&&
    ”折叠为
    Foo&
  • declval()
    的类型为
    Foo&
    (参考折叠:“
    Foo&&&&
    ”折叠为
    Foo&&
使用
添加左值参考

  • declval()
    将属于
    Foo&
    类型
  • declval()
    的类型为
    Foo&
    (参考折叠:“
    Foo&&
    ”折叠为
    Foo&
  • declval()
    的类型为
    Foo&
    (!)(参考折叠:“
    Foo&&&
    ”折叠为
    Foo&
也就是说,您永远不会得到
Foo&&

另外,
declval()
的类型是
Foo&&
(您可以编写
Foo&&rr=Foo();
但不能
Foo&lr=Foo();
)。而且那
declval()
会是
Foo&
的类型,只是感觉“不对”


编辑:由于您要求提供示例:

#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;
#包括
使用名称空间std;
结构A{};
结构B{};
结构C{};
福班{
公众:
Foo(int){}/(不可默认构造)
一个onLvalue()&{返回一个{};}
B onRvalue()&&{return B{};}
C onWhatever(){返回C{};}
};
decltype(declval().onLvalue())a;
decltype(declval().onRvalue())b;
decltype(declval().onWhatever())c;

如果使用了
declval
add\u lvalue\u reference
,则不能将
onRvalue()
与之一起使用(第二个
decltype
)。

您希望能够获取
t
const
//volatile其限定版本。由于可能没有复制或移动构造函数,因此不能只返回类型,即需要返回引用。另一方面,向引用类型添加右值引用没有效果

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

也就是说,添加一个右值引用类型会产生一个类似于传递类型的对象的结果

当您需要提供
noexcept
规范时,可以在my library中找到需要控制返回类型的示例。下面是一个典型的方法:

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;
}
friend T操作符+(常量T&lhs、常量U&rhs)
无异常(无异常(T(lhs),
std::declval()+=rhs,
T(std::declval())
{
nrv(lhs);
nrv+=rhs;
返回nrv;
}
在通用代码中,您需要对自己正在做的事情保持精确。在上述情况下,
T
U
是我无法控制的类型,
noexcept
对常量左值引用、非常量左值引用和右值引用副本的规范可能不同。因此,我需要能够表达如下情况:

  • 我可以从
    T&
    构造
    T
    ?(使用
    T(std::declval())
  • 我可以从
    常量T&
    构造
    T
    ?(使用
    T(std::declval())
  • 我可以从
    T&
    构造
    T
    ?(使用
    T(std::declval())

幸运的是,
std::declval
允许通过使用
std::add\u rvalue\u reference
并引用折叠规则实现上述功能。

类型名称生成器:)“将类型更改为对类型的引用,允许用户访问该类型的成员,而无需创建该类型的实例。”您在哪里读到的?当
Foo
是用户定义的类型时,除了成员函数中的引用匹配之外,是否还有其他理由将
declval
(或
declval
)与
declval
区分开来?
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;
}