2016-08-23 14 views
5

Chciałbym mieć schludny sposób inicjowania wielu odniesień zwracanych z funkcji poprzez std::tuple przy użyciu std::tie (lub std::forward_as_tuple) - zobacz kod zabawki poniżej.Inicjowanie wielu odniesień za pomocą std :: tie

#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; 
} 

roztworze powyżej działa, ale mający tymczasową std::tuple i wielokrotne std::get S jest irytujące i byłoby wspaniale móc tego uniknąć, jeśli to możliwe (na przykład po powrocie nie-referencje).

Problem polega na tym, że nie możemy mieć zwisających referencji, więc nie można zainicjować zmiennych wcześniej. Czy jest jakiś kreator C++ 11/C++ 14, który pozwala mi inicjować referencje, jak nazywam się std::tie? Czy też powyższe jedyne rozwiązanie?

+6

Byłbyś zainteresowany niektóre czary C++ 17? –

+0

Jeśli to jedyny sposób, to jestem uszy! – jaw

+6

Wygląda na to, że chcesz [Wiązania strukturalne] (http://stackoverflow.com/documentation/c%2b%2b/487/how-to-return-several-values-from-a-function/3384/structured-bindings # t = 201608231422577108315). Niestety musisz poczekać na C++ 17. – NathanOliver

Odpowiedz

7

W C++ 17 Wiązania strukturalne zapisują kod za Ciebie.

std::tuple<int &, int &> tmpTuple = foo.GetMembers(); 
int &x = std::get<0>(tmpTuple); 
int &y = std::get<1>(tmpTuple); 

jest mniej więcej taka sama jak

auto&[x,y] = foo.GetMembers(); 

(może mam drobne błędy składniowe w kodzie C++ 17, brak mi doświadczenia, ale masz pomysł.)

Ty może zrobić coś podobnego w C++ 14 ze stylem przekazywania kontynuacji i adapterem:

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)};} 

który, modulo typoes, daje:

cps(foo.GetMembers()) 
->*[&](int& x, int&y) 
{ 
    std::cout << x << " " << y << std::endl; 

    foo.IncrementMembers(); 
    std::cout << x << " " << y << std::endl; 
}; 

return 0; 

co jest dziwne. (Zauważ, że cps obsługuje funkcje, które zwracają pary lub std::array s i wszystko "podobne do krotki").

Naprawdę nie ma lepszego sposobu na poradzenie sobie z tym strukturalnym wiązaniem, które zostało dodane do C++ 17 z jakiegoś powodu.

Strasznych preprocesor Hack może być prawdopodobnie napisane, że wygląda tak:

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

ale objętość kodu byłaby duża, nie kompilator Znam pozwala debugować bałagan, że będzie generowany, masz wszystko dziwne dziwactwa, które powoduje przecięcie preprocesora i C++ itp.

+0

Przeczytałem, że gdy tylko możliwe zmienne zadeklarowane z powiązaniami strukturalnymi są aliasami dla poszczególnych elementów, a nie rzeczywistymi zmiennymi, więc może odniesienia nie są 't needed (np. 'x' jest w rzeczywistości' foo.m_memberInt'). Nie wiem, czy to sprawiło, że znalazło się w ostatecznej wersji i nie może znaleźć tam, gdzie to przeczytałem. – bolov

2

Samodzielne rozwiązanie do metaprogramowania preprocesora, zakwestionowane przez Yakk.

Kod dostosowany z mojej własnej biblioteki metaprogramowania lekkiego preprocesora vrm_pp.

Obsługuje 8 elementów krotki.

(To jest straszne. Niesamowity rodzaju straszne.)

#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&>{}, ""); 
} 

(Patrz wcześniejsze edycje na tej odpowiedzi dla wersji vrm_pp zgodny.)

+1

Co się stanie, jeśli moja krotka zostanie nazwana 'bob ()'? Nie zmusisz mnie do użycia * dodatkowych nawiasów * Czy jesteś? – Yakk

Powiązane problemy