C++ 从常量函数返回非常量属性引用

C++ 从常量函数返回非常量属性引用,c++,reference,C++,Reference,这个代码有什么问题吗 // Example program #include <iostream> #include <string> #include <vector> class A { public: A( std::vector<int>& ref ) : data(ref) {} std::vector<int>& getData() const { return data; } priv

这个代码有什么问题吗

// Example program
#include <iostream>
#include <string>
#include <vector>

class A
{
public:
    A( std::vector<int>& ref ) : data(ref) {}

    std::vector<int>& getData() const { return data; }

private:
    std::vector<int>& data;
};

void modifyA( const A& a )
{
    a.getData().push_back(3);
}

int main()
{
    std::vector<int> foo;
    A a(foo);
    modifyA(a);
    std::cout << foo.size() << std::endl;
    std::cout << foo.front() << std::endl;
}
//示例程序
#包括
#包括
#包括
甲级
{
公众:
A(std::vector&ref):数据(ref){}
std::vector&getData()常量{return data;}
私人:
std::向量和数据;
};
无效修改A(常数A和A)
{
a、 getData()。向后推_(3);
}
int main()
{
std::矢量foo;
A(foo);
修改a(a);

STD::CUT

这个代码很容易导致未定义的行为。考虑这个例子,根据你在评论中所说的:

struct B {
  std::vector<int> vec;
  A a;
  B() : vec{}, a{vec} {}
};

int main() {
  const B b;

  auto& vec = b.a.getData();

  vec.push_back(0);
}
结构B{ std::vec; A A; B():vec{},a{vec}{} }; int main(){ 常数B; auto&vec=b.a.getData(); 向量推回(0); }
我有一个const对象,它的所有成员都是const。但这里我得到了一个对这些const对象之一的非const引用。没有警告,没有明显的丑陋施法。只有沉默的鼻恶魔


即使所有权不是问题,这也是非常容易出错的。

< P>这个代码很容易导致未定义的行为。考虑这个例子,根据您在评论中所说的:

struct B {
  std::vector<int> vec;
  A a;
  B() : vec{}, a{vec} {}
};

int main() {
  const B b;

  auto& vec = b.a.getData();

  vec.push_back(0);
}
结构B{ std::vec; A A; B():vec{},a{vec}{} }; int main(){ 常数B; auto&vec=b.a.getData(); 向量推回(0); } 我有一个const对象,它的所有成员都是const。但这里我得到了一个对这些const对象之一的非const引用。没有警告,没有明显的丑陋施法。只有沉默的鼻恶魔



即使所有权从来都不是问题,这也是非常容易出错的。

谁拥有
a.data?
如何阻止它悬空?@Alexander:在原始代码中,̀a.data`属于在其成员函数中创建和销毁
a
实例的类。因此
a.data
保证保持不变
A
生命周期内的ve(如此处发布的示例所示)。我同意如果向量在a之前被破坏,这可能是个问题,但我不认为这是这里未定义行为的原因。我对此非常怀疑,如果我是你,我会进一步调查。老实说,给出的示例与那些“未定义行为”和“类成员被损坏”无关问题。基本上与存储(和返回)相同指向非常量数据的常量指针,即。在任何情况下,从常量成员函数返回非常量引用都是一个糟糕的想法。什么是“谁拥有
a.data?”
什么可以阻止它悬空?@Alexander:在原始代码中,̀a.data`属于在其成员函数中创建和销毁
a
实例的类。所以
A.data
保证在
A
生命周期内保持活动状态(如本文示例所示)。我同意如果向量在a之前被破坏,这可能是个问题,但我不认为这是这里未定义行为的原因。我对此非常怀疑,如果我是你,我会进一步调查。老实说,给出的示例与那些“未定义行为”和“类成员被损坏”无关问题。基本上与存储(和返回)相同指向非常量数据的常量指针,即。在任何情况下,从常量成员函数返回非常量引用都是一个糟糕的想法。也许这不是最佳的引用共享模式,但在给定的示例中没有任何类型的鼻魔。仅仅因为
b
被声明为const-qualified并不意味着禁止使用保留对其字段的非常量限定引用并对其进行修改。@VTT-修改最初声明为常量的对象是UB,即使该限定符最初出现在超级对象上也是如此。经典的鼻魔。如果您假定它不是,请使用规范性引用来支持它。@StoryTeller:在什么情况下会以UB结束。可以吗OS/编译器决定将const对象存储在一种“只读”中可能会阻止它通过如图所示检索的非常量引用被正确修改的内存?@jpo38-这是一种确切的方法。其他方法包括编译器供应商在手头有常量引用时如何优化。@StoryTeller标准提供了
可变的
解决方法。但在OP的示例中,甚至由于原始对象不是const-qualified对象,此具有const-qualified对象更改的潜在UB不会出现。也许这不是最佳的引用共享模式,但在给定示例中没有任何类型的鼻恶魔。仅仅因为
b
被声明为const-qualified并不意味着禁止持有非const限定了对其字段的引用并对其进行了修改。@VTT-修改最初声明为const的对象是UB,即使该限定符最初出现在超级对象上也是如此。经典的鼻魔。如果您假定它不是,请使用规范性引用来支持它。@StoryTeller:在什么情况下会以UB结束。OS/编译器可以er决定将const对象存储在一种“只读”中可能会阻止它通过如图所示检索的非常量引用被正确修改的内存?@jpo38-这是一种确切的方法。其他方法包括编译器供应商在手头有常量引用时如何优化。@StoryTeller标准提供了
可变的
解决方法。但在OP的示例中,甚至此具有常量限定对象更改的潜在UB不会出现,因为原始对象不是常量限定对象。