【问题标题】:How to correctly alias a base class member in C++?如何在 C++ 中正确地给基类成员起别名?
【发布时间】:2021-11-29 13:29:30
【问题描述】:

我写了一个这样的Edge 类:

struct Edge : public ::std::pair<int, int>
{
  using ::std::pair<int, int>::pair;
  int &src = first;
  int &dst = second;
};

namespace std
{
  template <>
  struct hash<Edge>
  {
    std::size_t operator()(Edge const &x) const noexcept
    {
      return (x.src << 16) | x.dst;
    }
  };
}

所以我可以用src/dst 代替first/second

但是,当我使用std::unordered_set 时,我发现出了问题。

Edge e(1, 2);
print_edge_debug_info(e);
using _set_t = std::unordered_set<Edge>;
_set_t s;
s.insert(e);
for(auto &&_e : s) {
  print_edge_debug_info(_e);
}
/*
output:
 (1, 2) [ 2,0x7ffca1c08724]  [ 2,0x7ffca1c08724] 
 (1, 2) [ 2,0x2bb80fc]  [ 2,0x7ffca1c08724]
*/

print_edge_debug_info:

inline void print_edge_debug_info(Edge const &edge)
{
  std::cout << edge
            << " "
            << "[ " << edge.second << "," << &edge.second << "] "
            << " "
            << "[ " << edge.dst << "," << &edge.dst << "] "
            << std::endl;
}

dstsecond 在构造第一个 Edge 对象后具有相同的地址。但是如果我把这个Edge 对象放到std::unordered_set 中并从集合中取出,结果的dstsecond 的地址是不同的。

此外,新的second 和旧的second 有不同的地址。但是新的dst 和旧的dst 具有相同的地址。这意味着新的dst 是旧second 的别名,而不是新second 的别名。

这对我来说似乎很奇怪。我不明白为什么会这样。

我使成员别名的方式错误吗?正确的方法是什么?

【问题讨论】:

  • 引用成员不是普通别名。它们是在复制、分配或移动时必须考虑的适当成员。添加对混合的引用会使the rule of 3 违规更加突出,我怀疑这里就是这种情况。为什么不直接使用成员函数访问器呢?它会为您省去很多麻烦。
  • @StoryTeller-UnslanderMonica 实际上,我昨天已经将它们更改为 getter/setter。我只是好奇我的代码发生了什么。所以我把我的旧代码(即上面的代码)写了一个简单的版本并研究它。
  • 为什么要继承std::pair?据我所知,写struct Edge {int src; int dst;}; 完全符合您的要求。

标签: c++ c++11 c++17


【解决方案1】:

以下较小的示例演示了所示方法的问题。

Edge a{1, 2};

// ...

Edge b=a;

asrcdst 引用引用 a.firsta.second,正如预期的那样。

bsrcdst 引用...也引用a.firsta.second,原因很简单,没有正当理由让它们成为其他任何东西,在这里。这就是你用你的装置观察到的。

避免意外意外的更好办法是一开始就避免使用引用:

int &src()
{
    return first;
}

int src() const
{
    return first;
}

(与second 相同)。这还具有不需要Edge 占用两倍内存的优点,只是为了携带引用。即使有正确的=,C++ 编译器也不太可能弄清楚如何优化它们。

在 C++ 中存在引用是有充分理由的,但它们并不是用来为类成员创建别名的。这不是他们的语义设计的目的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    • 1970-01-01
    • 2022-06-27
    • 1970-01-01
    相关资源
    最近更新 更多