你好 stackoverflow 社区,
我已经学习 C++ 几个月了,最近我一直在尝试掌握围绕“新”值类别、移动语义,尤其是临时物化的概念。
首先,如何解释“临时物化转换”这个词对我来说并不简单。转换部分对我来说很清楚(prvalue -> xvalue)。但在这种情况下,“临时”究竟是如何定义的呢?我曾经认为临时对象是仅存在的未命名对象 - 从语言的角度来看 - 直到评估它们所创建的表达式的最后一步。但这种概念似乎与临时对象的实际情况不符在临时物化、新价值类别等更广泛的背景下。
由于对“临时”一词缺乏明确性,导致我无法判断“临时物化”是临时物化还是临时物化。我认为是前者,但我不确定。另外:术语“临时”仅用于类类型吗?
这直接给我带来了下一个困惑:prvalues 和 xvalues 对于临时变量起什么作用?假设我有一个右值表达式,需要以必须将其转换为 xvalue 的方式进行计算,例如通过执行成员访问。究竟会发生什么?纯右值是否实际存在(在内存中或其他地方)并且纯右值是否已经是临时的?现在,“临时物化转换”被描述为“任何完整类型 T 的纯右值都可以转换为相同类型 T 的 xvalue。此转换通过使用临时对象评估纯右值,从纯右值初始化类型 T 的临时对象作为其结果对象,并生成一个表示临时对象的 xvalue”(https://en.cppreference.com/w/cpp/language/implicit_conversion)将纯右值转换为 xvalue。这段摘录让我认为纯右值是在内存或寄存器中任何地方都不存在的东西,直到它通过这种转换“具体化”为止。(另外,我不确定临时对象是否与临时对象相同。)因此,据我了解,此转换是通过对纯右值表达式进行求值来完成的,该表达式的结果是“真实”对象。然后,该对象由 xvalue 表达式表示(= 表示?)。记忆中发生了什么?右值在哪里,x值现在在哪里?
我的下一个问题是关于临时物化的某个部分的更具体的问题。在 Kris van Rens 在 YouTube ( https://www.youtube.com/watch?v=liAnuOfc66o&t=3576s ) 上大约 56:30 的演讲“Understanding valuecategories in C++”中,他展示了这张幻灯片:
根据 cppreference.com 关于临时物化的说法,数字 1 和 2 是明确的情况(1:类 pravlue 上的成员访问,2:将引用绑定到纯右值(如 std::string + 运算符中)。
不过,我对第三个不太确定。Cppreference 说:“请注意,当从相同类型的纯右值(通过直接初始化或复制初始化)初始化对象时,不会发生临时物化:此类对象是直接从初始化器初始化的。这确保了“保证复制省略”。 ” + 运算符返回纯右值。现在,这个 std::string 类型的纯右值用于初始化一个 auto(也应该解析为 std::string)变量。这听起来像是前面的 cppreference 摘录中讨论的情况。那么这里真的会出现临时实体化吗?由中间的 xvalue 表达式“表示”的对象(1 和 2)会发生什么情况?他们什么时候被摧毁?如果 + 运算符返回纯右值,它是否“存在”在某个地方?如果纯右值甚至不是真实的(物化?)对象,那么对象 auto …
呼唤void reset( pointer ptr = pointer() ) noexcept; 会调用以下操作
\n\n\n给定 current_ptr(由 *this 管理的指针),按以下顺序执行以下操作:
\n\n
\n- 保存当前指针的副本 old_ptr = current_ptr
\n- 使用参数 current_ptr = ptr 覆盖当前指针
\n- 如果旧指针非空,则删除先前管理的对象 if(old_ptr) get_deleter()(old_ptr)。
\n
这个特殊命令的原因是什么?为什么不先执行 3),然后再执行 2)?在这个问题中std::unique_ptr::reset 检查托管指针无效?第一个答案引用了标准
\n\n\n[\xe2\x80\xa6] [ 注意:这些操作的顺序很重要,因为调用 get_deleter() 可能会破坏 *this。\xe2\x80\x94结束注]
\n
这是唯一的原因吗?怎么能get_deleter()毁掉unique_ptr(*this )呢?
阅读C++ 命名需求:可交换我遇到了以下注释
当标准库函数执行交换时,未指定是否
<utility>实际包含它,因此用户提供的 swap() 不应期望包含它。
假设我有一个用户定义的类型class Foo和用户提供的swap(). 我想使用标准库算法来class Foo执行交换
using std::swap;
swap(arg1, arg2);
Run Code Online (Sandbox Code Playgroud)
如上述 cppreference 文章中所述,并且我必须
#include使用此算法的文件没有#include <utility>。
如果我依赖<utility>my 中提供的功能swap(),这怎么会导致问题呢?我必须#include <utility>自己在定义我的swap(). 该场景应类似于以下内容:我有一个文件,其中#includes包含class Foo用户提供的文件swap()以及我使用的算法的标头。因此,永远不存在算法是否#include <utility>这样做的问题。
#include我可能对编译和编译在某些不同场景中如何相互作用存在误解。哪种情况可能会导致这里出现编译问题?
我知道第二次过载std::forward:
template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;
Run Code Online (Sandbox Code Playgroud)
用于右值(如 Howard Hinnant 在他的回答中所述:How does std::forward receive the right argument?)
cppreference.com上有一个使用此重载的示例( Praetorian 的How does std::forward receive the right argument?中也提到了这一点):
- 将右值作为右值转发,并禁止将右值作为左值转发 此重载使得可以将表达式(例如函数调用)的结果(可能是右值或左值)转发为转发引用参数的原始值类别。
例如,如果包装器不仅转发其参数,还调用该参数的成员函数,并转发其结果:
Run Code Online (Sandbox Code Playgroud)// transforming wrapper template<class T> void wrapper(T&& arg) { foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get())); }其中 arg 的类型可以是
Run Code Online (Sandbox Code Playgroud)struct Arg { int i = 1; int get() && { return i; } // call to this overload is rvalue int& get() & { …
考虑以下代码:
struct S
{
S(int, double) {}
explicit S(const S&) {}
explicit S(S&&) {}
};
void i_take_an_S(S s) {}
S i_return_an_S() { return S{ 4, 2.0 }; }
int main()
{
i_take_an_S(i_return_an_S());
}
Run Code Online (Sandbox Code Playgroud)
使用“-std=c++17”标志,g++ 和 clang++ 都可以很好地编译此代码。然而,MSVC(带有 /std:c++17 标志)报告
“错误 C2664:'void i_take_an_S(S)':无法将参数 1 从 'S' 转换为 'S'”
作为编译错误,带有附加注释
“结构‘S’的构造函数被声明为‘显式’”。
根据C++17的初始化规则(第3点的解释)S,不应考虑使用复制构造函数来S初始化i_take_an_S;S(int, double)应该通过直接列表初始化选择精确匹配。
这可能是 MSVC 中的一个错误吗?
std::vector\xe2\x80\x98s 初始值设定项列表构造函数具有以下形式
vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );\nRun Code Online (Sandbox Code Playgroud)\n是什么使得这样的初始化std::vector<string> vec{ \xe2\x80\x9cfoo\xe2\x80\x9d, \xe2\x80\x9cbar\xe2\x80\x9d };成为可能?为什么构造函数接受一个std::initializer_list<const char*>,即使std::vectors\xe2\x80\x98sT是std::string?std::initializer_list<const char*>从到 的转换在哪里以及如何进行std::initializer_list<string>发生?
c++ implicit-conversion c++11 stdinitializerlist braced-init-list