আপনি কীভাবে একটি স্ট্যান্ড :: টিপল উপাদানগুলির পুনরাবৃত্তি করতে পারেন?


112

আমি কীভাবে একটি টিপল (সি ++ 11 ব্যবহার করে) এর মাধ্যমে পুনরাবৃত্তি করতে পারি? আমি নিম্নলিখিত চেষ্টা করেছিলাম:

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

তবে এটি কাজ করে না:

ত্রুটি 1: দুঃখিত, অসম্পূর্ণ: স্থির দৈর্ঘ্যের তর্ক তালিকায় 'শ্রোতা ...' প্রসারিত করতে পারে না।
ত্রুটি 2: আমি একটি ধ্রুবক প্রকাশে উপস্থিত হতে পারি না।

সুতরাং, আমি কীভাবে একটি টিপলের উপাদানগুলির উপর সঠিকভাবে পুনরাবৃত্তি করব?


2
আমি জিজ্ঞাসা করতে পারি, আপনি সি ++ 0 এক্স মধ্যে কীভাবে সংকলন করবেন? এটি যতটা জানি আমি প্রকাশিত বা প্রস্তুত হয় না।
বুখার্ড

5
g ++ সংস্করণ ৪.৩ থেকে ভেরিয়াদিক টেম্পলেট সহ কয়েকটি সি ++ 0 এক্স বৈশিষ্ট্যগুলির পরীক্ষামূলক সমর্থন ধারণ করে। অন্যান্য সংকলকরাও একই কাজ করে (বিভিন্ন বৈশিষ্ট্য সেট সহ, আপনি যদি তাদের উত্পাদনে ব্যবহার করতে চান তবে রক্তপাতের প্রান্তের জিনিসগুলির জন্য বিভিন্ন প্রকারের সমর্থন সহ আপনি 90 এ ফিরে এসেছেন)
এপ্রোগ্রামার

আমি g ++ সংস্করণ 4.4

9
এই প্রশ্নের একটি সি ++ 11 আপডেট দরকার।
সর্বজনীন

2
@ সর্বজনীন এখন, এটির জন্য একটি সি ++ 14 আপডেট দরকার
মরিচ_চিকো

উত্তর:


26

বুস্ট.ফিউশন একটি সম্ভাবনা:

অনাকাঙ্ক্ষিত উদাহরণ:

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

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

@ ভিক্টরসেহর আফিক্স এটি না (কমপক্ষে জিসিসি ৪.7.২ তে)? ইঙ্গিত দিয়ে কেউ?
সেপ্টেম্বর

@ViktorSehr পাওয়া সমস্যা: একটি বাগ / ভ্রান্তি লয় আচরণকে অন্তর্ভুক্ত অনুক্রম, নির্ভর করে দেখতে ঘটায় # 8418 বুস্ট টিকেট আরো বিস্তারিত জানার জন্য
sehe

এই কাজের জন্য বুস্ট :: ফিউশন :: টিউপল পরিবর্তে স্টুড :: টিপল ব্যবহার করা দরকার।
মার্সিন

জিসিসি ৮.১ / মিংডব্লু-64৪ এর অধীনে, বুস্ট :: ফিউশন :: স্ট্যান্ড লাম্বদা এক্সপ্রেশন সহ: ব্যবহারের জন্য দুটি সতর্কতা পেয়েছি: বুস্ট / এমপিএল / assert.hpp: 188: 21: সতর্কতা: 'assert_arg' এর ঘোষণায় অপ্রয়োজনীয় প্রথম বন্ধনী [-স্বীকৃতি] ব্যর্থ হয়েছে ************ (পূর্বে :: ************ উত্সাহ / এমপিএল / assert.hpp: 193: 21: সতর্কতা: এতে অপ্রয়োজনীয় প্রথম বন্ধনী 'assert_not_arg' এর ঘোষণা [-স্বীকৃতি] ব্যর্থ হয়েছে ************ (বুস্ট :: এমপিএল :: নয়_ <প্রেড> :: ************
হোসেইন

129

একটি টিপল ওভার আইট্রেটিংয়ের উপর ভিত্তি করে আমার একটি উত্তর আছে :

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

সাধারণ ধারণাটি হ'ল সংকলন সময় পুনরাবৃত্তি ব্যবহার করা। প্রকৃতপক্ষে, এই ধারণাটি এমন একটি প্রিন্টফ তৈরি করতে ব্যবহৃত হয় যা মূল টিপল পেপারগুলিতে উল্লিখিত সুরক্ষিত is

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
সুন্দর সহজ উদাহরণের জন্য ধন্যবাদ। এটি কীভাবে কাজ করে তার পটভূমি সন্ধানকারী সি ++ প্রবর্তকদের জন্য , SFINAE এবং enable_ifডকুমেন্টেশন দেখুন
ফাহিম মিঠা

এটি সহজেই জেনেরিক হতে সাধারণীকরণ করা যেতে পারে for_each। আসলে আমি নিজেই করেছি :-) আমি মনে করি এই উত্তরটি ইতিমধ্যে জেনারেলাইজড করা থাকলে আরও কার্যকর হবে।
সর্বময়

4
সেখানে আমি সাধারণীকরণটি যুক্ত করেছিলাম কারণ আমার আসলে একটি প্রয়োজন, এবং আমি মনে করি এটি অন্যদের দেখতে কার্যকর হবে।
সর্বময়

2
দ্রষ্টব্য: আপনার সাথে সংস্করণগুলিরও প্রয়োজন হতে পারে const std::tuple<Tp...>&.. যদি আপনি পুনরাবৃত্তি করার সময় টিপলগুলি সংশোধন করার পরিকল্পনা না করেন তবে এই constসংস্করণগুলি যথেষ্ট।
মারাত্মক-গিটার

2
লিখিত হিসাবে নয় .. আপনি সূচক উল্টিয়ে একটি সংস্করণ তৈরি করতে পারেন - I = আকার থেকে শুরু করুন ... (টিপি) এবং গণনা করুন। তারপরে স্পষ্টভাবে সর্বাধিক সংখ্যক আরোগুলি সরবরাহ করুন। আপনি একটি সংস্করণ তৈরি করতে পারেন যা কোনও ট্যাগ টাইপের উপর ভেঙে যায়, ব্রেক_t বলে। তারপরে আপনি মুদ্রণ বন্ধ করতে চাইলে আপনি এই ট্যাগ প্রকারের কোনও জিনিস আপনার টুপলে রেখে দেবেন। অথবা আপনি একটি টেম্পলেট পারম হিসাবে স্টপ প্রকার সরবরাহ করতে পারেন। স্পষ্টতই আপনি রান সময়ে বিরতি করতে পারেন না।
এমএসআর

54

সি ++ 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
@ হেলমেজো এটি কমা প্রকাশের জন্য ((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))এখানে বিস্তৃত হয়েছে।
xskxzr 20'19

মনে রাখবেন যে আপনি কমা-এক্সপ্রেশনতে বৈধ নয় এমন কাজগুলি করতে চান (যেমন ভেরিয়েবল এবং ব্লক ঘোষণা করে), আপনি সেগুলিকে কোনও পদ্ধতিতে রেখে ফোল্ড-কমা-এক্সপ্রেশন থেকে কেবল কল করতে পারেন।
মিরাল

24

সি ++ 17 এ আপনি এটি করতে পারেন:

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

এটি ইতিমধ্যে স্ট্যান্ড :: পরীক্ষামূলক :: প্রয়োগ ব্যবহার করে কলম্ব ++ 3.9 এ কাজ করে।


4
এটি কি পুনরাবৃত্তির দিকে নিয়ে যায় না - যেমন কলগুলি do_something()- একটি অনির্দিষ্ট ক্রমে ঘটে, কারণ প্যারামিটার প্যাকটি একটি ফাংশন কলের মধ্যে প্রসারিত হয় (), যার মধ্যে যুক্তিগুলি অনির্ধারিত ক্রম দিয়ে থাকে? এটি খুব তাৎপর্যপূর্ণ হতে পারে; আমি কল্পনা করছিলাম যে বেশিরভাগ লোকেরা অর্ডারটিকে সদস্যদের মতো একই ক্রমে, যেমন সূচকগুলি হিসাবে গ্যারান্টিযুক্ত হওয়ার প্রত্যাশা করবে std::get<>()। আফাইক, এরকম ক্ষেত্রে গ্যারান্টিযুক্ত অর্ডার পেতে, সম্প্রসারণটি অবশ্যই এর মধ্যেই করা উচিত {braces}। আমি কি ভূল? এই উত্তরটি এই জাতীয় আদেশের উপর জোর দেয়: স্ট্যাকওভারফ্লো.com
আন্ডারস্কোর_আডেম্বর

21

বুস্ট.হানা এবং জেনেরিক ল্যাম্বডাস ব্যবহার করুন:

#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
Bulletmagnet

3
@ বুলেটমেগনেট এখানে নীরবতার জন্য করা হয়েছিল এবং ADL কোনও সমস্যা ছাড়াই এটি পরিচালনা করতে পারে। এছাড়াও এটি স্থানীয়ভাবেও কাজ করে।
মরিচ_চিকো

16

সি ++ এই উদ্দেশ্যে প্রসারিত বিবৃতি প্রবর্তন করছে । তারা মূলত সি ++ ২০ এর জন্য ট্র্যাকে ছিলেন তবে ভাষা শব্দের পর্যালোচনার জন্য অভাবের কারণে সংক্ষিপ্তভাবে কাটাটি মিস করেছেন ( এখানে এবং এখানে দেখুন )।

বর্তমানে সম্মত সিনট্যাক্স (উপরের লিঙ্কগুলি দেখুন):

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

এটি সংকলিত-সময় পুনরাবৃত্তি, @ সেমসারের উপস্থাপিত অনুরূপ। তবে এটি SFINAE ব্যবহার করে না তাই (আমার মনে হয়) এটি আরও সংকলক-বান্ধব।


8

আপনাকে এখানে বুস্ট.টুপলের সাথে দেখানো টেমপ্লেট মেটাগ্রোগ্রামিং ব্যবহার করতে হবে:

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

সি ++ 0 এক্সে আপনি 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) ... );

দ্রষ্টব্য: সি ++ এ 14 make_index_sequenceটি ইতিমধ্যে সংজ্ঞায়িত হয়েছে ( http://en.cppreferences.com/w/cpp/utility/integer_sequence )।

যদি আপনার বাম-থেকে-ডান মূল্যায়ন আদেশের প্রয়োজন হয় তবে এর মতো কিছু বিবেচনা করুন:

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

এখানে কেবল স্ট্যান্ডার্ড লাইব্রেরি সহ টিপল আইটেমগুলির মাধ্যমে পুনরাবৃত্তি করার একটি সহজ সি ++ 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 ব্যবহার করতে চান এবং আপনার সি ++ সংকলক রয়েছে যা ভ্যারিয়্যাডিক টেম্পলেটগুলি সমর্থন করে, কোড বেলো চেষ্টা করুন (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;
}

বৈকল্পিক টেম্পলেটগুলির শক্তি!


আমি আপনার প্রথম সমাধানটি চেষ্টা করেছিলাম, তবে জোড়াতে এই ফাংশনটি ব্যর্থ হয়। কোনও ধারণা কেন? টেমপ্লেট <টাইপনেম টি, টাইপনাম ইউ> শূন্য সংযোজন (জোড় <টি, ইউ> পি) out কোট << পি.ফার্স্ট + পি.সেকেন্ড << এন্ডেল; main int main (int argc, char * argv []) out cout << "হ্যালো"। << শেষ; for_each (make_tuple (2,3,4), [] (int i) out cout << i << endl;}); for_each (make_tuple (Make_pair (1,2), Make_pair (3,4%), addt); প্রত্যাবর্তন 0; }
ব্যবহারকারী2023370

এই উত্তরটি এত ভার্বোজ লেখা কারণ এটি লজ্জাজনক কারণ আমি মনে করি পুনরাবৃত্তির উপায় (for_each_impl) আমার দেখা সমস্ত সমাধানগুলির মধ্যে সবচেয়ে মার্জিত।
জোকি

3

এমএসভিসি এসটিএলে একটি _ফোর্ড_এচ_টুপল_ইলেট ফাংশন রয়েছে (ডকুমেন্টেড নয়):

#include <tuple>

// ...

std::tuple<int, char, float> values{};
std::_For_each_tuple_element(values, [](auto&& value)
{
    // process 'value'
});

2

অন্যেরা কিছু সুনির্দিষ্টভাবে তৈরি তৃতীয় পক্ষের লাইব্রেরি উল্লেখ করেছেন যা আপনি ঘুরে দেখতে পারেন। তবে আপনি যদি তৃতীয় পক্ষের লাইব্রেরিগুলি ছাড়াই সি ++ ব্যবহার করেন তবে নীচের কোডটি সহায়তা করতে পারে।

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

দ্রষ্টব্য: কোডটি সি ++ 11 সরবরাহকারী যে কোনও সংকলক সহ কম্পাইল করে এবং এটি স্ট্যান্ডার্ড লাইব্রেরির ডিজাইনের সাথে ধারাবাহিকতা রাখে:

  1. টিপলটি হওয়া দরকার নেই std::tupleএবং পরিবর্তে এমন কিছু হতে পারে যা সমর্থন করে std::getএবং std::tuple_size; বিশেষত, std::arrayএবং std::pairব্যবহৃত হতে পারে;

  2. টিপল একটি রেফারেন্স টাইপ বা সিভি-যোগ্যতাসম্পন্ন হতে পারে;

  3. এটির অনুরূপ আচরণ রয়েছে std::for_eachএবং ইনপুটটি ফেরত দেয় UnaryFunction;

  4. সি ++ ১৪ (বা লাস্টার সংস্করণ) ব্যবহারকারীদের জন্য typename std::enable_if<T>::typeএবং typename std::decay<T>::typeতাদের সরলীকৃত সংস্করণ std::enable_if_t<T>এবং প্রতিস্থাপন করা যেতে পারে std::decay_t<T>;

  5. সি ++ 17 (বা লাস্টার সংস্করণ) ব্যবহারকারীদের জন্য, std::tuple_size<T>::valueএর সরলিকৃত সংস্করণ std::tuple_size_v<T>,।

  6. সি ++ ২০ (বা লাস্টার সংস্করণ) ব্যবহারকারীদের জন্য, SFINAEবৈশিষ্ট্যটি এর সাথে প্রয়োগ করা যেতে পারে Concepts


2

ব্যবহার constexprএবং if constexpr(সি ++ 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

আমি এখানে, এখানে এবং এখানে যে সমস্ত উত্তর দেখেছি তার মধ্যে আমি @ সিগিদাগি পছন্দ করেছি সেরা পুনরাবৃত্তির । দুর্ভাগ্যক্রমে, তার উত্তরটি খুব ভার্জোজ যা আমার মতে অন্তর্নিহিত স্পষ্টতাকে অস্পষ্ট করে।

এই যা আরও সংক্ষিপ্ত এবং সাথে কাজ করে তার সমাধান আমার সংস্করণ 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>()
    );
}

ডেমো: কলিরু

সি ++ 14 এর std::make_index_sequenceপ্রয়োগ করা যেতে পারে সি ++ 11


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 অপারেটরগুলি) এ পরিবর্তন করতে হবে to যদিও 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/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

@ স্টিপক্স উত্তরটি প্রসারিত করে আমরা তাদের সমাধান আরও জেনেরিক (সি ++ 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));
    }
}

তারপরে, আমাদের প্রতিটি ধরণের পরিদর্শন করার কৌশল প্রয়োজন need

কিছু সাহায্যকারীদের সাথে শুরু করা যাক (প্রথম দুটি সিপ্রেফারেন্স থেকে নেওয়া):

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;}
};
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.