为什么返回类型的非平凡析构函数会阻止尾部调用优化? 目前,在C++编译器中,尾调用优化的一个规则是返回类型必须是可破坏的。(基于对GCC、Clang trunk behavior的分析,MSVC对任何非平凡类型都有问题)

为什么返回类型的非平凡析构函数会阻止尾部调用优化? 目前,在C++编译器中,尾调用优化的一个规则是返回类型必须是可破坏的。(基于对GCC、Clang trunk behavior的分析,MSVC对任何非平凡类型都有问题),c++,c++17,tail-call-optimization,return-value-optimization,C++,C++17,Tail Call Optimization,Return Value Optimization,这项规定是否仍然必要?由于C++17返回值优化是强制性的,因此该函数似乎仍然可以使用trail调用优化,即使返回类型非常重要。这里的问题是什么,阻止编译器这样做 @编辑,代码示例: #include <string> bool h(); std::string g() { std::string s1 = "a", s2 = "b"; if (h()) return s1; else return s2; } st

这项规定是否仍然必要?由于C++17返回值优化是强制性的,因此该函数似乎仍然可以使用trail调用优化,即使返回类型非常重要。这里的问题是什么,阻止编译器这样做

@编辑,代码示例:

#include <string>

bool h();

std::string g() {
    std::string s1 = "a", s2 = "b";
    if (h()) return s1;
    else return s2;
}

std::string f() {
    return g();  // <= here I'd expect call-tail optimization due to RVO, since it is prvalue
}
#包括
boolh();
std::string g(){
std::字符串s1=“a”,s2=“b”;
如果(h())返回s1;
否则返回s2;
}
std::string f(){

return g();//返回值优化仅在返回临时值(更准确地说:a
prvalue
-另请参见)时才是必需的,例如,在这种情况下:

std::vector<int> foo() {
  return std::vector<int>{1,2};
}
std::vector<int> foo() {
  std::vector<int> myVec{1,2};
  return myVec;
}
std::vector foo(){ 返回std::向量{1,2}; }
如果您要返回一个本地对象,这被称为返回值优化(也称为“复制省略”),这不是强制性的(但在大多数情况下仍然执行),例如,在这种情况下:

std::vector<int> foo() {
  return std::vector<int>{1,2};
}
std::vector<int> foo() {
  std::vector<int> myVec{1,2};
  return myVec;
}
std::vector foo(){ std::vector myVec{1,2}; 返回myVec; } 在这种情况下,编译器可以首先在
foo()
s帧内构造
myVec
,通过复制返回,然后销毁。显然大多数编译器不会这样做。不过,当
foo()
结束时,可能会发生销毁


我想这就是为什么尾部调用优化需要平凡的析构函数的原因。如果
foo()
是尾部调用优化的,那么标准必须考虑通过复制/破坏来创建/返回的生命周期,因此有一种方法(平凡地)正在销毁返回的对象。

如果我理解正确,
f
可以替换为
g
(内联)(我猜是非强制性的)。并且
g
没有NRVO(或RVO)因为它有多个返回路径。gcc和clang都保存和还原$rdi->$rax。也许他们不认为
g
正确地设置了$rax?这与琐碎的可破坏性没有直接关系,而是与ABI是否允许通过寄存器有关。如果类型是琐碎的可破坏性但足够大,那么尾部调用也会被忽略未优化:@dyp,是的,这是我第一次听到关于(N)RVO的琐碎破坏。我认为不琐碎不是阻止(N)的借口RVO。隐含的想法是,如果首先省略了构造,则省略的销毁不会产生副作用,或者不重要。我几乎可以肯定,在寄存器中传递这些值取决于类型的微不足道的可销毁性。Clang[[Clang::musttail]]属性返回一个错误,即类型必须是平凡可破坏的,并且在许多平凡可撤销/位复制可移动的提议中使用了相同的措辞。经过一些研究,我在Herb Sutter/Niall Douglas的谈话中听到了相同的事情,如果我正确理解它们,这种小值ABI无效性是由非平凡的析构函数造成的。