将类型更改为reference
类型,允许访问该类型的成员而无需创建该类型的实例。这似乎适用于lvalue references
和rvalue references
。
declval
通过add_rvalue_reference
而不是 add_lvalue_reference
实施,
add_rvalue_reference
更合适的用例?编辑:
我想我有点模糊,这些答案都非常好,但触及稍微不同点。建议使用两种不同的答案,Howard强调您可以选择您的类型具有哪种参考,使add_rvalue_reference
更灵活。其他答案强调默认行为会自动选择更自然地反映输入类型的引用。我不知道该挑选什么!如果有人可以添加两个简单的例子,分别激励每个属性的需要,那么我会感到满意。
答案 0 :(得分:28)
使用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<Foo>()
属于Foo&&
类型的事实很好(您可以写Foo&& rr = Foo();
但不能Foo& lr = Foo();
)。 declval<Foo&&>()
类型为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;
如果declval
使用add_lvalue_reference
,则无法使用onRvalue()
(第二decltype
)。
答案 1 :(得分:7)
是的,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&
答案 2 :(得分:4)
您希望能够获得T
,T&
或const
/ volatile
限定版本。由于可能没有复制或移动构造函数,因此不能只返回类型,即需要返回引用。另一方面,将rvalue teference添加到引用类型无效;
std::declval<T> -> T&&
std::declval<T&> -> T&
也就是说,添加一个右值引用类型会产生一个看起来像传递类型的对象的结果!
答案 3 :(得分:1)
当您需要提供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;
}
在通用代码中,您需要完全关于您正在做的事情。在上面,T
和U
是我控制范围之外的类型,而noexcept
规范来自const左值引用,非const左值引用和右值引用的副本可能是不同。因此,我需要能够表达以下案例:
T
构建T&
吗? (使用T(std::declval<T&>())
)T
构建const T&
吗? (使用T(std::declval<const T&>())
)T
构建T&&
吗? (使用T(std::declval<T>())
)幸运的是,std::declval
允许使用std::add_rvalue_reference
并引用折叠规则来实现上述目标。