使用std :: tie初始化多个引用

时间:2016-08-23 14:19:14

标签: c++ c++11

我希望使用std::tuple(或std::tie通过std::forward_as_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时初始化引用?或者上面是唯一的解决方案?

2 个答案:

答案 0 :(得分:8)

在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 s以及任何&#34; tuple-like&#34;)的函数。

实际上还没有一种更好的方法来处理这种结构化绑定,其中有一个原因是添加到C ++ 17中。

可能会编写一个可怕的预处理程序黑客,如下所示:

BIND_VARS( foo.GetMembers(), x, y );

但是代码量很大,我知道没有编译器可以调试生成的混乱,你会得到预处理器和C ++交集引起的所有奇怪的怪癖等等。

答案 1 :(得分:1)

自包含的预处理器元编程解决方案,受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标准的版本,请参阅此答案的先前编辑。)

相关问题