【问题标题】:Name alias references for pair or tuple values名称对或元组值的别名引用
【发布时间】:2018-11-20 19:19:02
【问题描述】:

在重组一些代码时,我在返回具有 2 个值的结构时遇到了一个“问题”。现在这些确实应该以记录的效果命名。后来我想使用tie,所以我将结构更改为从std::pair 继承并仅设置引用。现在这实际上工作正常,但是您会注意到现在我的结构的大小为 24,而不是与这对相比只有 8。

#include <tuple>


struct Transaction : public std::pair<int, int> {
    using pair::pair;

  int& deducted = first;
  int& transfered = second;
};
//static_assert(sizeof(Transaction) == sizeof(std::pair<int, int>));//commenting in this line will fail compilation

Transaction makeTheTransaction();

void test(int& deduct, int& transfer) {
    std::tie(deduct, transfer) = makeTheTransaction(); 
}

可能显而易见的方法是更改​​为成员函数,但是对于这种情况来说,这也是太多的“样板”(以后不使用tie 会变得更容易)。直接的 memcpy 是例如。一个元组是直前 UB。直接结构化绑定也不可行,因为变量已在使用中。

我的问题是,不考虑可重用部分(并且考虑到大小不应超过 2 个整数),什么是更好或最少的代码解决方案?

更新:对于这种情况,我最终只是做了一个简单的结构并暂时保留返回。对于来到这里的其他用户,有一个库提案来提升似乎能够将任何结构转换为元组:https://github.com/apolukhin/magic_get/

【问题讨论】:

  • 简单:struct Transaction {int deducted; int transfered; }; ?
  • @Jarod42 可以,但它无法记录 makeTheTransaction() 返回的内容。
  • 为什么你必须从 std::pair 继承?您可以只让 makeTransaction 返回一个普通的旧对,因为您似乎没有使用“扣除”和“转移”字段(这不会像所写的那样工作,需要在构造函数中设置引用) .
  • 继承 std::pair 是 UB。
  • 我不明白为什么你需要从std::pair 继承才能使用std::tie。只需使用它:auto t = std::tie(transaction.deducted, transaction.transferred).

标签: c++ c++17 std-pair stdtuple structured-bindings


【解决方案1】:

看来你让我的问题过于复杂了。如果你需要使用std::tie,你可以使用它。无需更改您的结构:

struct Transaction
{
  int deducted;
  int transferred;
};

// later...

auto t = std::tie(transaction.deducted, transaction.transferred);

如果这是您经常使用的模式,那么您可以将其包装在一个小辅助方法中:

struct Transaction
{
  int deducted;
  int transferred;

  auto to_tuple() const
  {
    return std::tie(deducted, transferred);
  }
};

您也可以使用它来一次分配多个变量,尽管我强烈反对这样做。它容易出错并导致代码脆弱。 (例如,如果你在下面的例子中颠倒deducttransfer的顺序,你就有一个bug,但编译器不会给出警告或错误。)

void test(int& deduct, int& transfer)
{
  std::tie(deduct, transfer) = makeTheTransaction().to_tuple();
}

编辑:重新考虑...

如果这里的目标只是简单地将结构分解为变量,您可以直接这样做,避免使用对或元组:

struct Transaction
{
  int deducted;
  int transferred;

  void decompose(int* deducted_, int* transferred_)
  {
    *deducted_ = deducted;
    *transferred_ = transferred;
  }
};

void test(int& deduct, int& transfer)
{
  makeTheTransaction().decompose(&deduct, &transfer);
}

这仍然很脆弱,但至少现在当您编写对 decompose 方法的调用时,您将获得智能感知,这将使模式不太容易出错。

【讨论】:

  • 您对普通结构的第一个建议也是“痛苦的”。虽然可能不像你说的那样脆弱,但它会导致代码过于复杂。问题是,首先我必须创建并命名一个临时变量来保存返回的结构。然后我必须分配给其他 2 个变量 - 现在是 3 行代码。然后,为了不污染临时范围的其余部分,我必须在这 3 行周围放置一个块范围。
  • 好吧,我建议两件事。首先,打字不是瓶颈。为自己节省几行打字很少值得打开未来错误的可能性。我们应该争取通过构造正确的代码。其次,传递整个结构似乎是更简单的选择。为什么需要将结构体分解成多个变量?
  • 我同意通过结构更好地完成传递。但是,我发现,在处理现有(遗留)代码并对其进行重组时,有时出于不同的原因(例如,一个原因是不知道周围代码的大小)你只想一次采取一个或几个步骤需要更新 - 另一个并没有过多地失去你的注意力)。我现在最终只是做了简单的结构并返回到一个临时的(现在)。感谢您的反馈。
【解决方案2】:

我会选择:

struct Transaction
{
    int deducted;
    int transfered;
};

用法类似于:

Transaction makeTheTransaction() { return {4, 2}; }

int main()
{
    auto [deduct, transfer] = makeTheTransaction(); 
    std::cout << deduct << transfer << std::endl;
}

Demo

【讨论】:

  • 建议它是特定于 C++17 的?
  • 它必须与之前声明的变量一起工作。我不知何故忘记了这个问题..
  • 我更新了示例代码以显示测试函数通过参数返回
【解决方案3】:

添加转换功能有效:

#include <tuple>

struct Transaction {
    std::pair<int, int> data_;

    operator std::tuple<int &, int &> () {
        return std::tie(data_.first, data_.second);
    }
};
static_assert(sizeof(Transaction) == sizeof(std::pair<int, int>));

Transaction makeTheTransaction() {
    return Transaction();
}

void test(int& deduct, int& transfer) {
    std::tie(deduct, transfer) = makeTheTransaction();
}

我认为这不会在与 std::tie 一起使用时导致任何生命周期问题。

Transaction 不适用于具有两个标识符的结构化绑定。但是您可以通过专门为它指定std::getstd::tuple_size 使其支持“类元组”绑定。

【讨论】:

    猜你喜欢
    • 2012-08-26
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-07
    • 2013-04-25
    相关资源
    最近更新 更多