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;
}