使用std::tie初始化多个引用
Initializing multiple references with std::tie
我希望有一种简洁的方式,通过std::tie
(或std::forward_as_tuple
)初始化从一个函数通过std::tuple
返回的多个引用-参见下面的玩具代码。
#include <tuple>
#include <iostream>
class Foo
{
public:
Foo () : m_memberInt(5), m_anotherMemberInt(7) {}
void IncrementMembers() {++m_memberInt; ++m_anotherMemberInt;}
std::tuple<int &, int &> GetMembers() {return std::tie(m_memberInt, m_anotherMemberInt);}
private:
int m_memberInt;
int m_anotherMemberInt;
};
int main()
{
Foo foo;
// Can't have dangling references.
// int &x, &y;
// std::tie(x, y) = foo.GetMembers();
std::tuple<int &, int &> tmpTuple = foo.GetMembers();
int &x = std::get<0>(tmpTuple);
int &y = std::get<1>(tmpTuple);
std::cout << x << " " << y << std::endl;
foo.IncrementMembers();
std::cout << x << " " << y << std::endl;
return 0;
}
上面的解决方案是有效的,但是拥有临时的std::tuple
和多个std::get
是令人讨厌的,如果可能的话,能够避免这种情况(比如当返回非引用时)将是伟大的。
问题是我们不能有悬空引用,所以不能预先初始化变量。是否有一些c++ 11/c++ 14的魔法允许我初始化引用,因为我调用std::tie
?还是以上是唯一的解决方案?
在c++ 17中,结构化绑定为你编写代码。
std::tuple<int &, int &> tmpTuple = foo.GetMembers();
int &x = std::get<0>(tmpTuple);
int &y = std::get<1>(tmpTuple);
与
大致相同auto&[x,y] = foo.GetMembers();
(我可能在c++ 17代码中有轻微的语法错误,我缺乏经验,但你知道的。)
你可以在c++ 14中使用延续传递样式和适配器做类似的事情:
template<class Tuple>
struct continue_t {
Tuple&& tuple;
using count = std::tuple_size<std::remove_reference_t<Tuple>>;
using indexes = std::make_index_sequence<count{}>;
template<std::size_t...Is>
auto unpacker(std::index_sequence<Is...>) {
return [&](auto&& f)->decltype(auto){
using std::get; // ADL enabled
return decltype(f)(f)( get<Is>(std::forward<Tuple>(tuple))... );
};
};
template<class F>
decltype(auto) operator->*( F&& f )&& {
auto unpack = unpacker( indexes{} );
return unpack( std::forward<F>(f) );
}
};
template<class F>
continue_t<F> cps( F&& f ) {return {std::forward<F>(f)};}
,模类型,给你:
cps(foo.GetMembers())
->*[&](int& x, int&y)
{
std::cout << x << " " << y << std::endl;
foo.IncrementMembers();
std::cout << x << " " << y << std::endl;
};
return 0;
很奇怪。(注意,cps
支持返回对的函数,或者std::array
和任何类似元组的函数)。
一个可怕的预处理器黑客可能会写成这样:
BIND_VARS( foo.GetMembers(), x, y );
但是代码量会很大,我知道没有一个编译器可以让你调试可能产生的混乱,你会得到所有的奇怪的怪癖,预处理器和c++的交集,等等
自包含的预处理器元编程解决方案,受到Yakk的挑战。
代码改编自我自己的vrm_pp轻量级预处理器元编程库。
支持8个元组元素
(这太可怕了。)
#include <tuple>
#include <iostream>
#include <cassert>
#include <type_traits>
#define __INC_0 1
#define __INC_1 2
#define __INC_2 3
#define __INC_3 4
#define __INC_4 5
#define __INC_5 6
#define __INC_6 7
#define __INC_7 8
#define __INC_8 9
#define __NSEQ( m1, m2, m3, m4, m5, m6, m7, mN, ...) mN
#define __RSEQ() 7, 6, 5, 4, 3, 2, 1, 0
#define __CSEQ() 1, 1, 1, 1, 1, 1, 0, 0
#define __FOR_0(i, f, x)
#define __FOR_1(i, f, x, a0) f(i, x, a0)
#define __FOR_2(i, f, x, a0, a1) f(i, x, a0) __FOR_1(INC(i), f, x, a1)
#define __FOR_3(i, f, x, a0, a1, ...) f(i, x, a0) __FOR_2(INC(i), f, x, a1, __VA_ARGS__)
#define __FOR_4(i, f, x, a0, a1, ...) f(i, x, a0) __FOR_3(INC(i), f, x, a1, __VA_ARGS__)
#define __FOR_5(i, f, x, a0, a1, ...) f(i, x, a0) __FOR_4(INC(i), f, x, a1, __VA_ARGS__)
#define __FOR_6(i, f, x, a0, a1, ...) f(i, x, a0) __FOR_5(INC(i), f, x, a1, __VA_ARGS__)
#define __FOR_7(i, f, x, a0, a1, ...) f(i, x, a0) __FOR_6(INC(i), f, x, a1, __VA_ARGS__)
#define __FOR_8(i, f, x, a0, a1, ...) f(i, x, a0) __FOR_7(INC(i), f, x, a1, __VA_ARGS__)
#define __CAT_2(m0, m1) m0##m1
#define CAT_2(m0, m1) __CAT_2(m0, m1)
#define __INC(mX) __INC_##mX
#define INC(mX) __INC(mX)
#define __N_ARG(...) __NSEQ(__VA_ARGS__)
#define __ARGCOUNT(...)
__N_ARG(__VA_ARGS__, __RSEQ())
#define ARGCOUNT(...) __ARGCOUNT(__VA_ARGS__)
#define __FOR(f, x, ...)
CAT_2(__FOR_, ARGCOUNT(__VA_ARGS__))(
0, f, x, __VA_ARGS__)
#define FOR(...) __FOR(__VA_ARGS__)
#define REF_TIE_BODY(mIdx, x, mArg)
decltype(std::get<mIdx>(x)) mArg = std::get<mIdx>(x);
#define REF_TIE(tuple, ...)
FOR(REF_TIE_BODY, tuple, __VA_ARGS__)
int main()
{
int a = 0, b = 1;
std::tuple<int &, int &> tmpTuple{a, b};
REF_TIE(tmpTuple, aref, bref);
assert(a == aref);
assert(b == bref);
static_assert(std::is_same<decltype(aref), int&>{}, "");
static_assert(std::is_same<decltype(bref), int&>{}, "");
}
(请参阅此回答的先前编辑以获取vrm_pp兼容的版本)
相关文章:
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- 编译C++时未定义的引用
- Ctypes wstring通过引用传递
- c++r值引用应用于函数指针
- 理解c++中的引用
- C++取消引用指针.为什么会发生变化
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 强制转换为引用类型
- 引用一个已擦除类型(void*)的指针
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 为什么此元组到引用元组 (std::tie) 转换有效?
- std::tie 在从函数调用传递值时失败,并显示"无法绑定非常量左值引用"
- 分配给 std::tie 和引用元组有什么区别
- 使用std::tie初始化多个引用