आप एक std :: tuple के तत्वों पर कैसे पुनरावृति कर सकते हैं?


112

मैं एक ट्यूपल पर कैसे पुनरावृति कर सकता हूं (C ++ 11 का उपयोग करके)? मैंने निम्नलिखित कोशिश की:

for(int i=0; i<std::tuple_size<T...>::value; ++i) 
  std::get<i>(my_tuple).do_sth();

लेकिन यह काम नहीं करता है:

त्रुटि 1: क्षमा करें, अनइम्प्लीमेंट की गई: 'श्रोता ...' को एक निश्चित लंबाई तर्क सूची में विस्तारित नहीं कर सकता है।
त्रुटि 2: मैं एक स्थिर अभिव्यक्ति में प्रकट नहीं हो सकता।

तो, मैं टुपल के तत्वों पर सही ढंग से कैसे पुनरावृति करूं?


2
क्या मैं पूछ सकता हूं कि आप C ++ 0x में कैसे संकलन करते हैं? जहां तक ​​मुझे पता है यह न तो जारी है और न ही तैयार है।
बर्कहार्ड

5
संस्करण ४.३ के बाद से जी ++ में कुछ सी ++ ० एक्सएक्सएक्स विशेषताओं का प्रायोगिक समर्थन शामिल है, जिसमें वैरिएड टेम्पलेट शामिल हैं। अन्य संकलक भी ऐसा ही करते हैं (अलग-अलग फीचर सेट के साथ, यदि आप उन्हें उत्पादन में उपयोग करना चाहते हैं, तो आप 90 में वापस आ गए हैं, जो किनारे से खून बहने वाली चीजों की एक विस्तृत विविधता के साथ हैं)
APROgrammer

मैं g ++ संस्करण 4.4 का उपयोग std = c ++ 0x

9
इस प्रश्न को C ++ 11 अद्यतन की आवश्यकता है।
सर्वव्यापी

2
@Omnifarious को अब इसे
काली मिर्च_चिको

जवाबों:


26

Boost.Fusion एक संभावना है:

अप्रमाणित उदाहरण:

struct DoSomething
{
    template<typename T>
    void operator()(T& t) const
    {
        t.do_sth();
    }
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());

@ViktorSehr AFAICT यह (कम से कम जीसीसी 4.7.2 पर) नहीं करता है? संकेत के साथ कोई भी?
सेह

@ViktorSehr को यह समस्या मिली: बग / चूक से फ्यूजन का व्यवहार शामिल है, के आदेश पर निर्भर होने का कारण बनता है, अधिक जानकारी के लिए बूस्ट टिकट # 8418 देखें
seh

बूस्ट का उपयोग करने की आवश्यकता :: फ्यूजन :: एसटीडी के बजाय ट्यूपल :: इस काम के लिए ट्यूपल।
मार्सिन

GCC 8.1 / mingw-64 के तहत, मुझे बढ़ावा देने के लिए दो चेतावनी मिलती है :: fusion :: for_each with std lambda अभिव्यक्तियाँ: बढ़ावा / mpl / assert.hpp: 188: 21: 21: चेतावनी: 'assert_arg' की घोषणा में अनावश्यक कोष्ठक [-पैरेंटेशेस] विफल ************ (पूर्व :: ************ बढ़ावा / एमएलपी / assert.hpp: 193: 21: 21: चेतावनी: अनावश्यक कोष्ठक में 'assert_not_arg' की घोषणा [-वार्डेंटेस] विफल ************ (बढ़ावा :: mpl :: not_ <Pred> :: ************
होसैन

129

मेरे पास एक टपल पर Iterating पर आधारित उत्तर है :

#include <tuple>
#include <utility> 
#include <iostream>

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  { }

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  {
    std::cout << std::get<I>(t) << std::endl;
    print<I + 1, Tp...>(t);
  }

int
main()
{
  typedef std::tuple<int, float, double> T;
  T t = std::make_tuple(2, 3.14159F, 2345.678);

  print(t);
}

सामान्य विचार संकलन समय पुनरावृत्ति का उपयोग करना है। वास्तव में, इस विचार का उपयोग एक प्रिंटफ बनाने के लिए किया जाता है जो कि मूल टुपल पेपर में उल्लिखित सुरक्षित है।

इसे आसानी से for_eachटुपल्स के लिए सामान्यीकृत किया जा सकता है :

#include <tuple>
#include <utility> 

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

हालाँकि इसके बाद कुछ प्रयास करने की आवश्यकता होती है, FuncTजिसमें टुपल शामिल हो सकने वाले हर प्रकार के लिए उपयुक्त अधिभार के साथ कुछ का प्रतिनिधित्व करते हैं। यह सबसे अच्छा काम करता है यदि आप जानते हैं कि सभी टपल तत्व एक समान आधार वर्ग या कुछ समान साझा करेंगे।


5
अच्छे सरल उदाहरण के लिए धन्यवाद। C ++ शुरुआती लोगों के लिए पृष्ठभूमि की तलाश है कि यह कैसे काम करता है, SFINAE और enable_ifप्रलेखन देखें
फहीम मिष्ठा

यह आसानी से एक सामान्य होने के लिए सामान्यीकृत किया जा सकता है for_each। वास्तव में, मैंने इसे स्वयं किया। :-) मुझे लगता है कि अगर यह पहले से ही सामान्यीकृत होता तो यह उत्तर अधिक उपयोगी होता।
सर्वव्यापी

4
वहां, मैंने सामान्यीकरण को जोड़ा क्योंकि मुझे वास्तव में एक की आवश्यकता थी, और मुझे लगता है कि यह दूसरों के लिए उपयोगी होगा।
सर्वव्यापी

2
नोट: आपको इसके साथ संस्करणों की भी आवश्यकता हो सकती है const std::tuple<Tp...>&.. यदि आप पुनरावृति करते समय ट्यूपल्स को संशोधित करने का इरादा नहीं रखते हैं, तो वे constसंस्करण पर्याप्त होंगे।
घातक-गिटार

2
के रूप में लिखा नहीं है .. आप अनुक्रमण फ़्लिप के साथ एक संस्करण बना सकते हैं - I = sizeof ... (Tp) पर प्रारंभ करें और नीचे गिनें। फिर स्पष्ट रूप से अधिकतम संख्या में आर्ग्स की आपूर्ति करें। आप एक संस्करण भी बना सकते हैं जो एक टैग प्रकार पर टूट गया, ब्रेक_ट का कहना है। तब आप अपने टपल में उस टैग प्रकार की एक वस्तु डालेंगे, जब आप मुद्रण रोकना चाहते थे। या आप एक टेंपर्ड पार्म के रूप में स्टॉप टाइप की आपूर्ति कर सकते हैं। जाहिर है कि आप रन टाइम पर नहीं टूट सकते।
एम्सआर

55

C ++ 17 में, आप गुना अभिव्यक्ति केstd::apply साथ उपयोग कर सकते हैं :

std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);

टपल प्रिंट करने के लिए एक पूर्ण उदाहरण:

#include <tuple>
#include <iostream>

int main()
{
    std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
    std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}

[कोलिरु पर ऑनलाइन उदाहरण]

यह समाधान एम। अलागन के जवाब में मूल्यांकन आदेश के मुद्दे को हल करता है ।


1
क्या आप बता सकते हैं कि यहाँ क्या हो रहा है ((std::cout << args << '\n'), ...);:? लैम्ब्डा को एक बार ट्यूपल-तत्वों के रूप में अनपैक किया जाता है args, लेकिन डबल कोष्ठक के साथ क्या हो रहा है?
हेल्मेसो

4
@helmesjo यह ((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))यहाँ एक अल्पविराम अभिव्यक्ति तक फैलता है।
xskxzr

ध्यान दें कि यदि आप कॉमा-एक्सप्रेशन (जैसे चरों और ब्लॉकों को घोषित करना) में कानूनी नहीं हैं, तो आप उन सभी को एक विधि में डाल सकते हैं और इसे फोल्ड-कॉमा-एक्सप्रेशन के भीतर से कॉल कर सकते हैं।
मिरल

24

C ++ 17 में आप ऐसा कर सकते हैं:

std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);

यह पहले से ही Clang ++ 3.9 में काम करता है, std :: प्रयोगात्मक :: apply का उपयोग करता है।


4
क्या यह पुनरावृत्ति की ओर नहीं ले जाता है - अर्थात do_something()अनिर्दिष्ट आदेश में होने वाली कॉल - क्योंकि पैरामीटर पैक को एक फ़ंक्शन कॉल के भीतर विस्तारित किया जाता है (), जिसमें तर्क ने अनिर्दिष्ट आदेश दिया है? यह बहुत महत्वपूर्ण हो सकता है; मुझे लगता है कि अधिकांश लोगों को यह उम्मीद होगी कि ऑर्डर की गारंटी सदस्यों के समान ही होगी, यानी कि सूचकांकों के रूप में std::get<>()। AFAIK, इस तरह के मामलों में गारंटीकृत आदेश प्राप्त करने के लिए, विस्तार भीतर किया जाना चाहिए {braces}। क्या मै गलत हु? यह उत्तर इस तरह के आदेश देने पर जोर देता है: stackoverflow.com/a/16387374/2757035
अंडरस्कोर_ड

21

Boost.Hana और जेनेरिक लैम्ब्डा का प्रयोग करें:

#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>

struct Foo1 {
    int foo() const { return 42; }
};

struct Foo2 {
    int bar = 0;
    int foo() { bar = 24; return bar; }
};

int main() {
    using namespace std;
    using boost::hana::for_each;

    Foo1 foo1;
    Foo2 foo2;

    for_each(tie(foo1, foo2), [](auto &foo) {
        cout << foo.foo() << endl;
    });

    cout << "foo2.bar after mutation: " << foo2.bar << endl;
}

http://coliru.stacked-crooked.com/a/27b3691f55caf271


4
कृपया कृपया using namespace boost::fusion(विशेषकर साथ में using namespace std) मत जाओ । अब यह जानने का कोई तरीका नहीं है कि क्या for_eachहै std::for_eachयाboost::fusion::for_each
बुलेटमैग्नेट

3
@Bulletmagnet यह यहाँ मरोड़ के लिए किया गया था और ADL एक समस्या के बिना संभाल सकता है। इसके अलावा, यह भी समारोह स्थानीय है।
काली मिर्च_ मिर्च 16

16

C ++ इस उद्देश्य के लिए विस्तार कथन प्रस्तुत कर रहा है । वे मूल रूप से C ++ 20 के लिए ट्रैक पर थे, लेकिन भाषा के शब्दों की समीक्षा ( यहां और यहां देखें ) के लिए समय की कमी के कारण कटौती से चूक गए ।

वर्तमान में सहमत सिंटैक्स (ऊपर लिंक देखें) है:

{
    auto tup = std::make_tuple(0, 'a', 3.14);
    template for (auto elem : tup)
        std::cout << elem << std::endl;
}

15

C ++ 17 में ऐसा करने का एक और अधिक सरल, सहज और संकलक-अनुकूल तरीका if constexpr:

// prints every element of a tuple
template<size_t I = 0, typename... Tp>
void print(std::tuple<Tp...>& t) {
    std::cout << std::get<I>(t) << " ";
    // do things
    if constexpr(I+1 != sizeof...(Tp))
        print<I+1>(t);
}

यह संकलन-समय की पुनरावृत्ति है, @emsr द्वारा प्रस्तुत एक के समान है। लेकिन यह SFINAE का उपयोग नहीं करता है (इसलिए मुझे लगता है) यह अधिक संकलक-अनुकूल है।


8

आपको Boost.Tuple के साथ यहां टेम्प्लेट मेटाप्रोग्रामिंग का उपयोग करने की आवश्यकता है:

#include <boost/tuple/tuple.hpp>
#include <iostream>

template <typename T_Tuple, size_t size>
struct print_tuple_helper {
    static std::ostream & print( std::ostream & s, const T_Tuple & t ) {
        return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t );
    }
};

template <typename T_Tuple>
struct print_tuple_helper<T_Tuple,0> {
    static std::ostream & print( std::ostream & s, const T_Tuple & ) {
        return s;
    }
};

template <typename T_Tuple>
std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) {
    return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t );
}

int main() {

    const boost::tuple<int,char,float,char,double> t( 0, ' ', 2.5f, '\n', 3.1416 );
    print_tuple( std::cout, t );

    return 0;
}

C ++ 0x में, आप print_tuple()इसके बजाय एक वैरिएड टेम्पलेट फ़ंक्शन के रूप में लिख सकते हैं ।


8

पहले कुछ सूचकांक सहायकों को परिभाषित करें:

template <size_t ...I>
struct index_sequence {};

template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

अपने कार्य के साथ आप प्रत्येक टपल तत्व पर आवेदन करना चाहेंगे:

template <typename T>
/* ... */ foo(T t) { /* ... */ }

तुम लिख सकते हो:

template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    std::tie(foo(std::get<I>(ts)) ...);
}

template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
    return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}

या अगर fooरिटर्न void, उपयोग करें

std::tie((foo(std::get<I>(ts)), 1) ... );

नोट: C ++ 14 make_index_sequenceपर पहले से ही परिभाषित है ( http://en.cppreference.com/w/cpp/utility/integer_fterence )।

यदि आपको बाएं से दाएं मूल्यांकन क्रम की आवश्यकता है, तो इस तरह से कुछ पर विचार करें:

template <typename T, typename ...R>
void do_foo_iter(T t, R ...r) {
    foo(t);
    do_foo(r...);
}

void do_foo_iter() {}

template<typename ...T, size_t ...I>
void do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    do_foo_iter(std::get<I>(ts) ...);
}

template <typename ...T>
void do_foo(std::tuple<T...> &ts) {
    do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}

1
के रिटर्न मान डाली चाहिए fooकरने के लिए voidलागू करने से पहले operator,संभव रोग ऑपरेटर ओवरलोडिंग से बचने के लिए।
यक - एडम नेवेरुमोंट

7

यहाँ सिर्फ मानक पुस्तकालय के साथ टपल आइटम पर पुनरावृति का एक आसान C ++ 17 तरीका है:

#include <tuple>      // std::tuple
#include <functional> // std::invoke

template <
    size_t Index = 0, // start iteration at 0 index
    typename TTuple,  // the tuple type
    size_t Size =
        std::tuple_size_v<
            std::remove_reference_t<TTuple>>, // tuple size
    typename TCallable, // the callable to bo invoked for each tuple item
    typename... TArgs   // other arguments to be passed to the callable 
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
    if constexpr (Index < Size)
    {
        std::invoke(callable, args..., std::get<Index>(tuple));

        if constexpr (Index + 1 < Size)
            for_each<Index + 1>(
                std::forward<TTuple>(tuple),
                std::forward<TCallable>(callable),
                std::forward<TArgs>(args)...);
    }
}

उदाहरण:

#include <iostream>

int main()
{
    std::tuple<int, char> items{1, 'a'};
    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
    });
}

आउटपुट:

1
a

यह तब तक लूप को सशर्त रूप से तोड़ने के लिए बढ़ाया जा सकता है जब कॉल करने योग्य मान लौटाता है (लेकिन फिर भी ऐसे कॉलबल्स के साथ काम करते हैं जो बूल असाइन करने योग्य मान नहीं लौटाते हैं, जैसे शून्य):

#include <tuple>      // std::tuple
#include <functional> // std::invoke

template <
    size_t Index = 0, // start iteration at 0 index
    typename TTuple,  // the tuple type
    size_t Size =
    std::tuple_size_v<
    std::remove_reference_t<TTuple>>, // tuple size
    typename TCallable, // the callable to bo invoked for each tuple item
    typename... TArgs   // other arguments to be passed to the callable 
    >
    void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
    if constexpr (Index < Size)
    {
        if constexpr (std::is_assignable_v<bool&, std::invoke_result_t<TCallable&&, TArgs&&..., decltype(std::get<Index>(tuple))>>)
        {
            if (!std::invoke(callable, args..., std::get<Index>(tuple)))
                return;
        }
        else
        {
            std::invoke(callable, args..., std::get<Index>(tuple));
        }

        if constexpr (Index + 1 < Size)
            for_each<Index + 1>(
                std::forward<TTuple>(tuple),
                std::forward<TCallable>(callable),
                std::forward<TArgs>(args)...);
    }
}

उदाहरण:

#include <iostream>

int main()
{
    std::tuple<int, char> items{ 1, 'a' };
    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
    });

    std::cout << "---\n";

    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
        return false;
    });
}

आउटपुट:

1
a
---
1

5

यदि आप std :: tuple का उपयोग करना चाहते हैं और आपके पास C ++ कंपाइलर है जो कि वैरेडिक टेम्प्लेट का समर्थन करता है, तो कोड bellow (g ++ 4.5 के साथ परीक्षण) का प्रयास करें। यह आपके प्रश्न का उत्तर होना चाहिए।

#include <tuple>

// ------------- UTILITY---------------
template<int...> struct index_tuple{}; 

template<int I, typename IndexTuple, typename... Types> 
struct make_indexes_impl; 

template<int I, int... Indexes, typename T, typename ... Types> 
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> 
{ 
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; 
}; 

template<int I, int... Indexes> 
struct make_indexes_impl<I, index_tuple<Indexes...> > 
{ 
    typedef index_tuple<Indexes...> type; 
}; 

template<typename ... Types> 
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> 
{}; 

// ----------- FOR EACH -----------------
template<typename Func, typename Last>
void for_each_impl(Func&& f, Last&& last)
{
    f(last);
}

template<typename Func, typename First, typename ... Rest>
void for_each_impl(Func&& f, First&& first, Rest&&...rest) 
{
    f(first);
    for_each_impl( std::forward<Func>(f), rest...);
}

template<typename Func, int ... Indexes, typename ... Args>
void for_each_helper( Func&& f, index_tuple<Indexes...>, std::tuple<Args...>&& tup)
{
    for_each_impl( std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...);
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>&& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

बढ़ावा :: फ्यूजन एक और विकल्प है, लेकिन इसके लिए अपने स्वयं के टपल प्रकार की आवश्यकता होती है: बढ़ावा :: फ्यूजन :: टपल। बेहतर मानक से चिपके रहते हैं! यहाँ एक परीक्षण है:

#include <iostream>

// ---------- FUNCTOR ----------
struct Functor 
{
    template<typename T>
    void operator()(T& t) const { std::cout << t << std::endl; }
};

int main()
{
    for_each( std::make_tuple(2, 0.6, 'c'), Functor() );
    return 0;
}

Varadic टेम्पलेट्स की शक्ति!


मैंने आपके पहले समाधान की कोशिश की, लेकिन यह जोड़े पर इस फ़ंक्शन के साथ विफल रहता है। कोई भी विचार क्यों; टेम्प्लेट <टाइपनेम टी, टाइपनेम यू> शून्य व्यसनी (जोड़ी <टी, यू> पी) {cout << p.first + p.second << endl; } int main (int argc, char * argv []) {cout << "हैलो।" << एंडल; for_each (make_tuple (2,3,4), [] (int i) {cout << i << endl;}); for_each (make_tuple (make_pair (1,2), make_pair (3,4)), addt); वापसी 0; }
user2023370

यह शर्म की बात है कि यह उत्तर इसलिए लिखा गया है क्योंकि मुझे लगता है कि iterating (for_each_impl) का तरीका मेरे द्वारा देखे गए सभी समाधानों में से सबसे सुरुचिपूर्ण है।
जोकी


2

दूसरों ने कुछ अच्छी तरह से डिज़ाइन की गई थर्ड-पार्टी लाइब्रेरीज़ का उल्लेख किया है जिन्हें आप चालू कर सकते हैं। हालाँकि, यदि आप उन तृतीय-पक्ष पुस्तकालयों के बिना C ++ का उपयोग कर रहे हैं, तो निम्न कोड मदद कर सकता है।

namespace detail {

template <class Tuple, std::size_t I, class = void>
struct for_each_in_tuple_helper {
  template <class UnaryFunction>
  static void apply(Tuple&& tp, UnaryFunction& f) {
    f(std::get<I>(std::forward<Tuple>(tp)));
    for_each_in_tuple_helper<Tuple, I + 1u>::apply(std::forward<Tuple>(tp), f);
  }
};

template <class Tuple, std::size_t I>
struct for_each_in_tuple_helper<Tuple, I, typename std::enable_if<
    I == std::tuple_size<typename std::decay<Tuple>::type>::value>::type> {
  template <class UnaryFunction>
  static void apply(Tuple&&, UnaryFunction&) {}
};

}  // namespace detail

template <class Tuple, class UnaryFunction>
UnaryFunction for_each_in_tuple(Tuple&& tp, UnaryFunction f) {
  detail::for_each_in_tuple_helper<Tuple, 0u>
      ::apply(std::forward<Tuple>(tp), f);
  return std::move(f);
}

नोट: कोड किसी भी संकलक के साथ संकलित है C ++ 11, और यह मानक पुस्तकालय के डिजाइन के साथ स्थिरता रखता है:

  1. टपल की आवश्यकता नहीं है std::tuple, और इसके बजाय कुछ भी हो सकता है जो समर्थन करता है std::getऔर std::tuple_size; विशेष रूप से, std::arrayऔर std::pairइस्तेमाल किया जा सकता है;

  2. ट्यूपल एक संदर्भ प्रकार या सीवी-योग्य हो सकता है;

  3. इसके समान व्यवहार है std::for_each, और इनपुट लौटाता है UnaryFunction;

  4. C ++ 14 (या अंतिम संस्करण) उपयोगकर्ताओं के लिए, typename std::enable_if<T>::type और typename std::decay<T>::typeउनके सरलीकृत संस्करण के साथ प्रतिस्थापित किया जा सकता है, std::enable_if_t<T>और std::decay_t<T>;

  5. C ++ 17 (या अंतिम संस्करण) उपयोगकर्ताओं के लिए, std::tuple_size<T>::value , इसकी सरलीकृत संस्करण के साथ प्रतिस्थापित किया जा सकता std::tuple_size_v<T>

  6. C ++ 20 (या अंतिम संस्करण) उपयोगकर्ताओं के लिए, इस SFINAEसुविधा को इसके साथ लागू किया जा सकता है Concepts


2

का उपयोग कर constexprऔर if constexpr(C ++ 17) यह काफी सरल और सीधे आगे है:

template <std::size_t I = 0, typename ... Ts>
void print(std::tuple<Ts...> tup) {
  if constexpr (I == sizeof...(Ts)) {
    return;
  } else {
    std::cout << std::get<I>(tup) << ' ';
    print<I+1>(tup);
  }
}

1

मैं शायद इस ट्रेन से चूक गया था, लेकिन यह भविष्य के संदर्भ के लिए यहां होगा।
यहाँ मेरा निर्माण इस उत्तर पर और इस जिस्ट पर आधारित है :

#include <tuple>
#include <utility>

template<std::size_t N>
struct tuple_functor
{
    template<typename T, typename F>
    static void run(std::size_t i, T&& t, F&& f)
    {
        const std::size_t I = (N - 1);
        switch(i)
        {
        case I:
            std::forward<F>(f)(std::get<I>(std::forward<T>(t)));
            break;

        default:
            tuple_functor<I>::run(i, std::forward<T>(t), std::forward<F>(f));
        }
    }
};

template<>
struct tuple_functor<0>
{
    template<typename T, typename F>
    static void run(std::size_t, T, F){}
};

फिर आप इसे निम्नानुसार उपयोग करें:

template<typename... T>
void logger(std::string format, T... args) //behaves like C#'s String.Format()
{
    auto tp = std::forward_as_tuple(args...);
    auto fc = [](const auto& t){std::cout << t;};

    /* ... */

    std::size_t some_index = ...
    tuple_functor<sizeof...(T)>::run(some_index, tp, fc);

    /* ... */
}

इसमें सुधार की गुंजाइश हो सकती है।


ओपी के कोड के अनुसार, यह यह बन जाएगा:

const std::size_t num = sizeof...(T);
auto my_tuple = std::forward_as_tuple(t...);
auto do_sth = [](const auto& elem){/* ... */};
for(int i = 0; i < num; ++i)
    tuple_functor<num>::run(i, my_tuple, do_sth);

1

मैंने यहाँ और यहाँ देखे सभी उत्तरों में से , मुझे @sigidagi पसंद है का तरीका सबसे अच्छा है। दुर्भाग्य से, उनका जवाब बहुत ही मौखिक है जो मेरी राय में निहित स्पष्टता को अस्पष्ट करता है।

यह उनके समाधान का मेरा संस्करण है जो अधिक संक्षिप्त है और साथ काम करता है std::tuple, std::pairऔर std::array

template<typename UnaryFunction>
void invoke_with_arg(UnaryFunction)
{}

/**
 * Invoke the unary function with each of the arguments in turn.
 */
template<typename UnaryFunction, typename Arg0, typename... Args>
void invoke_with_arg(UnaryFunction f, Arg0&& a0, Args&&... as)
{
    f(std::forward<Arg0>(a0));
    invoke_with_arg(std::move(f), std::forward<Args>(as)...);
}

template<typename Tuple, typename UnaryFunction, std::size_t... Indices>
void for_each_helper(Tuple&& t, UnaryFunction f, std::index_sequence<Indices...>)
{
    using std::get;
    invoke_with_arg(std::move(f), get<Indices>(std::forward<Tuple>(t))...);
}

/**
 * Invoke the unary function for each of the elements of the tuple.
 */
template<typename Tuple, typename UnaryFunction>
void for_each(Tuple&& t, UnaryFunction f)
{
    using size = std::tuple_size<typename std::remove_reference<Tuple>::type>;
    for_each_helper(
        std::forward<Tuple>(t),
        std::move(f),
        std::make_index_sequence<size::value>()
    );
}

डेमो: कोलीरू

C ++ 14 को C ++ 11 के लिएstd::make_index_sequence लागू किया जा सकता है ।


0

बूस्ट का टपल सहायक कार्य प्रदान करता है get_head()और get_tail()इसलिए आपके सहायक कार्य इस तरह दिख सकते हैं:

inline void call_do_sth(const null_type&) {};

template <class H, class T>
inline void call_do_sth(cons<H, T>& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); }

जैसा कि यहां बताया गया है http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html

इसके साथ भी std::tupleऐसा ही होना चाहिए।

दरअसल, दुर्भाग्य std::tupleसे ऐसा इंटरफ़ेस प्रदान करने के लिए प्रतीत नहीं होता है, इसलिए पहले सुझाए गए तरीकों को काम करना चाहिए, या आपको उन स्विच करने की आवश्यकता होगी boost::tupleजिनके अन्य लाभ हैं (जैसे कि io ऑपरेटर पहले से ही प्रदान किए गए हैं)। हालांकि boost::tupleजीसीसी के साथ नकारात्मक पक्ष है - यह अभी तक वैरेडिक टेम्प्लेट स्वीकार नहीं करता है, लेकिन यह पहले से ही तय हो सकता है क्योंकि मेरे पास मेरी मशीन पर इंस्टॉल किए गए बढ़ावा का नवीनतम संस्करण नहीं है।


0

मैं एक ही समस्या पर ठोकर खाई है, जो कि फंक्शन ऑब्जेक्ट्स की अधिकता के लिए है, इसलिए यहां एक और उपाय दिया गया है:

#include <tuple> 
#include <iostream>

// Function objects
class A 
{
    public: 
        inline void operator()() const { std::cout << "A\n"; };
};

class B 
{
    public: 
        inline void operator()() const { std::cout << "B\n"; };
};

class C 
{
    public:
        inline void operator()() const { std::cout << "C\n"; };
};

class D 
{
    public:
        inline void operator()() const { std::cout << "D\n"; };
};


// Call iterator using recursion.
template<typename Fobjects, int N = 0> 
struct call_functors 
{
    static void apply(Fobjects const& funcs)
    {
        std::get<N>(funcs)(); 

        // Choose either the stopper or descend further,  
        // depending if N + 1 < size of the tuple. 
        using caller = std::conditional_t
        <
            N + 1 < std::tuple_size_v<Fobjects>,
            call_functors<Fobjects, N + 1>, 
            call_functors<Fobjects, -1>
        >;

        caller::apply(funcs); 
    }
};

// Stopper.
template<typename Fobjects> 
struct call_functors<Fobjects, -1>
{
    static void apply(Fobjects const& funcs)
    {
    }
};

// Call dispatch function.
template<typename Fobjects>
void call(Fobjects const& funcs)
{
    call_functors<Fobjects>::apply(funcs);
};


using namespace std; 

int main()
{
    using Tuple = tuple<A,B,C,D>; 

    Tuple functors = {A{}, B{}, C{}, D{}}; 

    call(functors); 

    return 0; 
}

आउटपुट:

A 
B 
C 
D

0

एक अन्य विकल्प टुपल्स के लिए पुनरावृत्तियों को लागू करना होगा। इसका लाभ यह है कि आप मानक लाइब्रेरी और लूप्स के लिए रेंज-आधारित द्वारा प्रदान किए गए विभिन्न एल्गोरिदम का उपयोग कर सकते हैं। इस पर एक सुंदर दृष्टिकोण यहाँ समझाया गया है https://foonathan.net/2017/03/03/tuple-iterator/ । मूल विचार ट्यूपल्स को पुनरावृत्तियों के साथ रेंज begin()और end()तरीकों में बदलना है। पुनरावृत्ति करने वाला स्वयं वापस लौटता है, std::variant<...>जिसे फिर उपयोग करके देखा जा सकता है std::visit

यहाँ कुछ उदाहरण हैं:

auto t = std::tuple{ 1, 2.f, 3.0 };
auto r = to_range(t);

for(auto v : r)
{
    std::visit(unwrap([](auto& x)
        {
            x = 1;
        }), v);
}

std::for_each(begin(r), end(r), [](auto v)
    {
        std::visit(unwrap([](auto& x)
            {
                x = 0;
            }), v);
    });

std::accumulate(begin(r), end(r), 0.0, [](auto acc, auto v)
    {
        return acc + std::visit(unwrap([](auto& x)
        {
            return static_cast<double>(x);
        }), v);
    });

std::for_each(begin(r), end(r), [](auto v)
{
    std::visit(unwrap([](const auto& x)
        {
            std::cout << x << std::endl;
        }), v);
});

std::for_each(begin(r), end(r), [](auto v)
{
    std::visit(overload(
        [](int x) { std::cout << "int" << std::endl; },
        [](float x) { std::cout << "float" << std::endl; },
        [](double x) { std::cout << "double" << std::endl; }), v);
});

मेरा कार्यान्वयन (जो ऊपर दिए गए लिंक में स्पष्टीकरण पर आधारित है):

#ifndef TUPLE_RANGE_H
#define TUPLE_RANGE_H

#include <utility>
#include <functional>
#include <variant>
#include <type_traits>

template<typename Accessor>
class tuple_iterator
{
public:
    tuple_iterator(Accessor acc, const int idx)
        : acc_(acc), index_(idx)
    {

    }

    tuple_iterator operator++()
    {
        ++index_;
        return *this;
    }

    template<typename T>
    bool operator ==(tuple_iterator<T> other)
    {
        return index_ == other.index();
    }

    template<typename T>
    bool operator !=(tuple_iterator<T> other)
    {
        return index_ != other.index();
    }

    auto operator*() { return std::invoke(acc_, index_); }

    [[nodiscard]] int index() const { return index_; }

private:
    const Accessor acc_;
    int index_;
};

template<bool IsConst, typename...Ts>
struct tuple_access
{
    using tuple_type = std::tuple<Ts...>;
    using tuple_ref = std::conditional_t<IsConst, const tuple_type&, tuple_type&>;

    template<typename T>
    using element_ref = std::conditional_t<IsConst,
        std::reference_wrapper<const T>,
        std::reference_wrapper<T>>;

    using variant_type = std::variant<element_ref<Ts>...>;
    using function_type = variant_type(*)(tuple_ref);
    using table_type = std::array<function_type, sizeof...(Ts)>;

private:
    template<size_t Index>
    static constexpr function_type create_accessor()
    {
        return { [](tuple_ref t) -> variant_type
        {
            if constexpr (IsConst)
                return std::cref(std::get<Index>(t));
            else
                return std::ref(std::get<Index>(t));
        } };
    }

    template<size_t...Is>
    static constexpr table_type create_table(std::index_sequence<Is...>)
    {
        return { create_accessor<Is>()... };
    }

public:
    static constexpr auto table = create_table(std::make_index_sequence<sizeof...(Ts)>{}); 
};

template<bool IsConst, typename...Ts>
class tuple_range
{
public:
    using tuple_access_type = tuple_access<IsConst, Ts...>;
    using tuple_ref = typename tuple_access_type::tuple_ref;

    static constexpr auto tuple_size = sizeof...(Ts);

    explicit tuple_range(tuple_ref tuple)
        : tuple_(tuple)
    {
    }

    [[nodiscard]] auto begin() const 
    { 
        return tuple_iterator{ create_accessor(), 0 };
    }

    [[nodiscard]] auto end() const 
    { 
        return tuple_iterator{ create_accessor(), tuple_size };
    }

private:
    tuple_ref tuple_;

    auto create_accessor() const
    { 
        return [this](int idx)
        {
            return std::invoke(tuple_access_type::table[idx], tuple_);
        };
    }
};

template<bool IsConst, typename...Ts>
auto begin(const tuple_range<IsConst, Ts...>& r)
{
    return r.begin();
}

template<bool IsConst, typename...Ts>
auto end(const tuple_range<IsConst, Ts...>& r)
{
    return r.end();
}

template <class ... Fs>
struct overload : Fs... {
    explicit overload(Fs&&... fs) : Fs{ fs }... {}
    using Fs::operator()...;

    template<class T>
    auto operator()(std::reference_wrapper<T> ref)
    {
        return (*this)(ref.get());
    }

    template<class T>
    auto operator()(std::reference_wrapper<const T> ref)
    {
        return (*this)(ref.get());
    }
};

template <class F>
struct unwrap : overload<F>
{
    explicit unwrap(F&& f) : overload<F>{ std::forward<F>(f) } {}
    using overload<F>::operator();
};

template<typename...Ts>
auto to_range(std::tuple<Ts...>& t)
{
    return tuple_range<false, Ts...>{t};
}

template<typename...Ts>
auto to_range(const std::tuple<Ts...>& t)
{
    return tuple_range<true, Ts...>{t};
}


#endif

रीड-ओनली पहुंच भी एक गुजर द्वारा समर्थित है const std::tuple<>&करने के लिए to_range()


0

@Stypox उत्तर पर विस्तार करते हुए, हम उनके समाधान को अधिक सामान्य (C ++ 17 आगे) बना सकते हैं। कॉल करने योग्य फ़ंक्शन तर्क जोड़कर:

template<size_t I = 0, typename... Tp, typename F>
void for_each_apply(std::tuple<Tp...>& t, F &&f) {
    f(std::get<I>(t));
    if constexpr(I+1 != sizeof...(Tp)) {
        for_each_apply<I+1>(t, std::forward<F>(f));
    }
}

फिर, हमें प्रत्येक प्रकार की यात्रा करने के लिए एक रणनीति की आवश्यकता है।

कुछ सहायकों के साथ शुरू करते हैं (पहले दो cppreference से लिया गया है):

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template<class ... Ts> struct variant_ref { using type = std::variant<std::reference_wrapper<Ts>...>; };

variant_ref का उपयोग ट्यूपल्स की स्थिति को संशोधित करने की अनुमति देने के लिए किया जाता है।

उपयोग:

std::tuple<Foo, Bar, Foo> tuples;

for_each_apply(tuples,
               [](variant_ref<Foo, Bar>::type &&v) {
                   std::visit(overloaded {
                       [](Foo &arg) { arg.foo(); },
                       [](Bar const &arg) { arg.bar(); },
                   }, v);
               });

परिणाम:

Foo0
Bar
Foo0
Foo1
Bar
Foo1

पूर्णता के लिए, यहां मेरा Barऔर Foo:

struct Foo {
    void foo() {std::cout << "Foo" << i++ << std::endl;}
    int i = 0;
};
struct Bar {
    void bar() const {std::cout << "Bar" << std::endl;}
};
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.