C++11 为什么局部变量的常量会抑制返回值的移动语义? 上面的例子说明了在微软Visual C++ 11中实现的C++ 2012中,函数的内部细节可以修改其返回类型。直到今天,我的理解是,程序员只需要知道返回类型的声明,就可以理解如何处理返回值,例如,当返回值作为参数传递给后续函数调用时并非如此。

C++11 为什么局部变量的常量会抑制返回值的移动语义? 上面的例子说明了在微软Visual C++ 11中实现的C++ 2012中,函数的内部细节可以修改其返回类型。直到今天,我的理解是,程序员只需要知道返回类型的声明,就可以理解如何处理返回值,例如,当返回值作为参数传递给后续函数调用时并非如此。,c++11,return,constants,move-semantics,rvalue-reference,C++11,Return,Constants,Move Semantics,Rvalue Reference,我喜欢在适当的地方创建局部变量const。它帮助我理清思路,清晰地构建算法。但是要小心返回声明为const的变量!即使变量不再被访问(毕竟执行了return语句),即使声明为const的变量早已超出范围(参数表达式的计算已完成),它也无法移动,因此将被复制(如果无法复制,则无法编译) 这个问题与另一个问题有关。区别在于,在后者中,函数被声明为返回一个const值。在我的示例中,FuncUsingConst被声明为返回一个volatile临时变量。然而,函数体的实现细节会影响返回值的类型,并确定返

我喜欢在适当的地方创建局部变量
const
。它帮助我理清思路,清晰地构建算法。但是要小心返回声明为
const
的变量!即使变量不再被访问(毕竟执行了
return
语句),即使声明为
const
的变量早已超出范围(参数表达式的计算已完成),它也无法移动,因此将被复制(如果无法复制,则无法编译)

这个问题与另一个问题有关。区别在于,在后者中,函数被声明为返回一个
const
值。在我的示例中,
FuncUsingConst
被声明为返回一个volatile临时变量。然而,函数体的实现细节会影响返回值的类型,并确定返回值是否可以用作其他函数的参数

该行为是否符合标准要求?
这怎么会有用呢?

附加问题:考虑到调用和实现可能在不同的翻译单元中,编译器如何知道编译时的差异


编辑:试图重新表述问题

函数的结果怎么可能比声明的返回类型更重要?函数声明不足以确定函数返回值的行为,这似乎是可以接受的吗?在我看来,这似乎是FUBAR的一个案例,我只是不确定是该归咎于该标准还是微软的实施

作为被调用函数的实现者,我甚至不可能知道所有的调用方,更不用说监视调用代码中的每一个小更改了。另一方面,作为调用函数的实现者,我不能依靠被调用函数不返回恰好在函数实现范围内声明为const的变量

函数声明是一个契约。现在它值多少钱?这里我们不是在讨论语义等价的编译器优化,比如复制省略,它很好,但不会改变代码的含义。无论是否调用copy-ctor,都会改变代码的含义(甚至会破坏代码,使其无法编译,如上所示)。要理解我在这里讨论的尴尬之处,请考虑上面的“奖金问题”。 我喜欢在适当的地方将局部变量设置为常量。它帮助我理清思路,清晰地构建算法

这确实是一种良好的做法。尽可能使用
const
。但是,在这里,您不能(如果您希望从中移动
const
对象)

在函数中声明一个
const
对象的事实是,只要对象处于活动状态,对象的状态就不会改变,换句话说,在调用其析构函数之前,对象的状态永远不会改变。甚至在调用其析构函数之前。只要对象处于活动状态,
const
对象的状态就不会改变

但是,在这里,您不知何故希望该对象在其因超出范围而被破坏之前立即移动,并且移动正在改变状态。您不能从
常量
对象移动,即使您不再使用该对象也不行

但是,您可以创建一个非
const
对象,并仅通过绑定到该对象的
const
引用在函数中访问它:

struct STest : public boost::noncopyable {
    STest(STest && test) : m_n( std::move(test.m_n) ) {}
    explicit STest(int n) : m_n(n) {}
    int m_n;
};

STest FuncUsingConst(int n) {
    STest const a(n);
    return a;
}

STest FuncWithoutConst(int n) {
    STest a(n);
    return a;
}

void Caller() {
    // 1. compiles just fine and uses move ctor
    STest s1( FuncWithoutConst(17) );

    // 2. does not compile (cannot use move ctor, tries to use copy ctor)
    STest s2( FuncUsingConst(17) );
}
通过一点规范,您可以轻松地强制执行以下语义:函数在创建对象后不应修改该对象,除非在返回时允许从该对象移动

更新:

正如评论中所建议的,另一种可能性是将常量引用绑定到非常量临时对象(其寿命将根据§12.2/5延长),并在返回时执行
常量转换

STest FuncUsingConst(int n) {
    STest object_not_to_be_touched_if_not_through_reference(n);
    STest const& a = object_not_to_be_touched_if_not_through_reference;

    // Now work only with a

    return object_not_to_be_touched_if_not_through_reference;
}
STest functusingconst(int n){
STest const&a=STest();
//现在只与一个
返回常量cast(std::move(a));
}
如果隐式使用了对象的复制/移动构造函数[…],并且无法访问特殊成员函数,则程序的格式是错误的

--N385C++草案标准[类] / 30 < /P> 我怀疑你的问题是MSVC 2012,而不是C++11

此代码,即使没有调用它,也是不合法的C++11:

STest FuncUsingConst(int n) {
    STest const& a = STest();

    // Now work only with a

    return const_cast<STest&&>(std::move(a));
}
因为没有合法的方法将
a
转换为返回值。虽然可以省略返回,但是省略返回值并不能消除复制构造函数存在的要求


如果MSVC2012允许编译
FuncUsingConst
,那么它这样做违反了C++11标准。

在不查看Std的情况下,我认为应该同时调用copy-ctor和move-ctor。是否确实只调用了copy ctor?只有当表达式是非易失性自动对象(函数或catch子句参数除外)的名称,且其cv非限定类型与函数返回类型“[class.copy]/31”相同时,才能省略为
return
语句创建的临时变量。这里不是这种情况,因此在
FuncUsingConst
中,对象
astruct STest {
  STest(STest const&) = delete
  STest(STest && test) : m_n( std::move(test.m_n) ) {}
  explicit STest(int n) : m_n(n) {}
  int m_n;
};

STest FuncUsingConst(int n) {
  STest const a(n);
  return a;
}