如何创建一个多次使用值的宏,而不复制它?

Cla*_*diu 5 c++ macros undefined-behavior c++11 forwarding-reference

我想创建一个宏,将一对解包为两个局部变量.如果它只是一个变量,我想不创建该对的副本,这将实现:

#define UNPACK_PAIR(V1, V2, PAIR) \
    auto& V1 = PAIR.first; \
    auto& V2 = PAIR.second;

UNPACK_PAIR(one, two, x);
Run Code Online (Sandbox Code Playgroud)

但是,我也希望它不要评估多次给出的表达式,例如这应该只调用expensive_computation()一次:

UNPACK_PAIR(one, two, expensive_computation());
Run Code Online (Sandbox Code Playgroud)

如果我做:

#define UNPACK_PAIR_A(V1, V2, PAIR) \
    auto tmp = PAIR; \
    auto& V1 = tmp.first; \
    auto& V2 = tmp.second;
Run Code Online (Sandbox Code Playgroud)

然后它适用于expensive_computation()案例,但它在x案件中复制.如果我做:

#define UNPACK_PAIR_R(V1, V2, PAIR) \
    auto& tmp = PAIR; \
    auto& V1 = tmp.first; \
    auto& V2 = tmp.second;
Run Code Online (Sandbox Code Playgroud)

然后它在x没有复制的情况下工作,但在这种expensive_computation()情况下失败.如果我做:

#define UNPACK_PAIR_CR(V1, V2, PAIR) \
    const auto& tmp = PAIR; \
    auto& V1 = tmp.first; \
    auto& V2 = tmp.second;

#define UNPACK_PAIR_RR(V1, V2, PAIR) \
    auto&& tmp = PAIR; \
    auto& V1 = tmp.first; \
    auto& V2 = tmp.second;
Run Code Online (Sandbox Code Playgroud)

这些都是编译和运行,但我怀疑它们会调用未定义的行为 - 我对此是否正确?另外,这些中的任何一个都有意义吗?

#define UNPACK_PAIR_RR(V1, V2, PAIR) \
    auto&& tmp = std::move(PAIR); \
    auto& V1 = tmp.first; \
    auto& V2 = tmp.second;

#define UNPACK_PAIR_RR(V1, V2, PAIR) \
    auto&& tmp = std::forward<decltype(PAIR)>(PAIR); \
    auto& V1 = tmp.first; \
    auto& V2 = tmp.second;
Run Code Online (Sandbox Code Playgroud)

有没有办法创建一个适用于这两种用例的宏 - x在给定表达式或函数调用的结果时,还没有复制还没有调用未定义的行为?

Lig*_*ica 6

你不需要一个宏.

auto p = std::make_pair(2, 3);
int x, y;
std::tie(x, y) = p;
Run Code Online (Sandbox Code Playgroud)

如果要引用一对现有成员:

auto p = std::make_pair(2, 3);
auto& x = p.first;
auto& y = p.second;
Run Code Online (Sandbox Code Playgroud)

而已.

现在,您可以继续进行更具挑战性/有趣/重要的事情.

  • 除非类型不是默认构造的. (2认同)
  • @Claudiu我明白,但是试图模仿另一种语言并且它的成语导致糟糕的编码习惯,导致不可读/不可维护的代码和(如已经提到的)一般没有意义(同样,一般来说也是不可能的).学习新语言,而不是试图"模拟"你最喜欢的语言. (2认同)
  • @Claudiu请问为什么你没有用Python编写这个特定的程序,因为它似乎具有你正在寻找的所有功能? (2认同)

Rei*_*ica 4

auto&&创建一个转发引用,即它接受任何内容。它并不总是)创建右值引用。所以就这样做:

#define UNPACK_PAIR(V1, V2, PAIR) \
    auto&& tmp = PAIR; \
    auto& V1 = tmp.first; \
    auto& V2 = tmp.second;
Run Code Online (Sandbox Code Playgroud)

但是,我强烈建议反对这种做法(除非使用的范围UNPACK_PAIR非常有限并且该操作在该范围内确实普遍存在)。它看起来像默默无闻,对我来说没有任何真正的好处。想象一下 6 个月后返回该项目,只用了两个小时就发现了一个关键错误。您会感谢自己使用非标准的基于宏的语法而不是可读的语法吗?

  • @Claudiu是的,该临时文件(`get_foo()`的返回值)的生命周期将被延长。 (2认同)
  • 这会用 `tmp` 污染当前作用域;例如,在一个范围内使用该宏两次将导致错误。 (2认同)