【问题标题】:Fixing overloads without breaking clients在不破坏客户端的情况下修复过载
【发布时间】:2020-06-24 07:07:38
【问题描述】:

这是我目前正在研究的一个玩具示例

class Foo
{
   private:
     std::string s;
     std::vector<std::string> v;
   public:

      // Foo constructors and other big 3

      std::string const& strmember() const;
      std::vector<std::string> const& vecmember() const;
      std::vector<std::string>& vecmember();
};

到目前为止一切顺利。要求来了,我必须添加重载并更改 Foo 的胆量,如下所示

class Foo
{
   private:
     std::u16string ssname;
     std::vector<std::u16string> vvname;
   public:

      // Foo constructors and other big 3

      std::string const& strmember() const;
      std::vector<std::string> const& vecmember() const;
      std::vector<std::string>& vecmember();

      std::u16string const& strmember(int) const;
      std::vector<std::u16string> const& vecmember(int) const;
      std::vector<std::u16string>& vecmember(int);
};

我必须保留老成员并为新成员添加重载,以便老客户不会中断。执行适当的字符转换,以便将旧的构造函数委托给新的构造函数,并根据新设计更改提供的成员。

如下实现这些成员

std::string const& Foo::strmember() const
{
    const auto& name = char_convert(ssname);
    return name; 
}
std::vector<std::string>& Foo::vecmember()
{
    return vecmember();
} 
std::vector<std::string>& Foo::vecmember()
{
    std::vector<std::string> result;
    std::transform(vvname.begin(),vvname.end(),std::back_inserter(result),[] (const std:u16string& in) -> std::string
                                                            {
                                                                return char_convert(in);
                                                            });
    return result;
}

std::u16string const& Foo::strmember(int) const
{
    return ssname;
}

std::vector<std::u16string> const& Foo::vecmember(int) const
{
    return vvname;
}

std::vector<std::u16string>& Foo::vecmember(int)
{
    return vvname;
}

当我尝试编译更改的代码时,编译器会给出以下警告和错误

error: reference to local variable ‘result’ returned [-Werror=return-local-addr]
     std::vector<std::string> result;
                              ^~~~~~
foo.cpp: In member function ‘const string& foo::strmember() const’:
foo.cpp: error: function returns address of local variable [-Werror=return-local-addr]
     return name;
            ^~~~
foo.cpp: note: declared here
     const auto& name = char_convert(ssname);
                                                 ^
cc1plus: all warnings being treated as errors

我该如何解决这个问题?我无法更改界面,因为它可能会破坏单元测试和客户端。

如何提供vecmember()strmember() 函数的mutator 和accessor 版本?

【问题讨论】:

  • 如果你的成员函数返回一个引用,那么,正如错误消息所说,你不能返回一个本地(严格来说是允许的,但会导致未定义的行为,所以你很幸运你的编译器是报告错误)。一种方法是保留旧的类成员,并返回对它们的引用。如果您坚持删除这些成员并通过引用返回.... 好吧,您不能吃蛋糕也不能吃。另一种选择是更改函数,以便它们按值返回。
  • 根据您的客户如何使用您的访问器,代理对象可能是一种变通方法。不过非常值得怀疑。

标签: c++ c++11 g++


【解决方案1】:

当函数超出范围时,函数的局部变量会被销毁。返回对此类变量的引用是未定义的行为(当函数返回时引用变得悬空)。

我想强调另一个同类型的问题。当你写:

const auto& name = char_convert(ssname); // Bad (undefined behaviour)

您正在绑定对右值的引用。 char_convert() 返回的临时 std::string 将在该语句之后被销毁。所以name 在被strmember() 函数返回之前就悬空了。
您可能应该改写(复制初始化):

auto name = char_convert(ssname); // Better

现在,如果您不想破坏兼容性,我认为您需要使用代理类(如 @Fureeish 所建议的那样)来处理底层的转换。
Here你可以看一个如何编写代理类的例子(如果需要的话)。


编辑:

正如 @Jarod42 在 cmets 中提到的那样,const auto&amp; name = char_convert(ssame); 确实是正确的,因为当临时对象绑定到引用时,它的生命周期将延长到引用本身的生命周期(直到 name 消失在我们的例子中是范围)。

但请记住,name 不是指ssname,而是指由char_convert(ssname) 返回的临时值(所以它在语法上是正确的,但它仍然没有按照您的意愿执行)。

无论如何,在函数返回后,未定义的行为仍然会发生。

【讨论】:

  • const auto&amp; name = char_convert(ssname); 在延长寿命时是正确的(以变量 name 结尾,因此在函数结束时,因此“不能”作为 (const) 引用返回)。
  • @Jarod42 哦,我错过了这一点,你说得对,我会编辑我的答案(但请不要删除你的评论)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-29
  • 2016-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多