কীভাবে বৈকল্পিক টেম্পলেট আর্গুমেন্ট সংরক্ষণ করবেন?


89

পরে ব্যবহারের জন্য কোনও প্যারামিটার প্যাক সংরক্ষণ করা কি সম্ভব?

template <typename... T>
class Action {
private:        
    std::function<void(T...)> f;
    T... args;  // <--- something like this
public:
    Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
    void act(){
        f(args);  // <--- such that this will be possible
    }
}

তারপরে পরে:

void main(){
    Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);

    //...

    add.act();
}

4
হ্যাঁ; একটি টিউপল সঞ্চয় করুন এবং কল করতে কিছু সূচি প্যাক ব্যবহার করুন use
কেরেক এসবি

উত্তর:


67

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

std::tuple<Ts...> args;

তদ্ব্যতীত, আপনাকে আপনার নির্মাতাকে কিছুটা পরিবর্তন করতে হবে। বিশেষত, argsএকটি দিয়ে আরম্ভ করা std::make_tupleএবং আপনার প্যারামিটার তালিকায় সর্বজনীন রেফারেন্সের অনুমতি দেওয়া:

template <typename F, typename... Args>
Action(F&& func, Args&&... args)
    : f(std::forward<F>(func)),
      args(std::forward<Args>(args)...)
{}

তদুপরি, আপনাকে অনেকটা এর মতো সিকোয়েন্স জেনারেটর স্থাপন করতে হবে:

namespace helper
{
    template <int... Is>
    struct index {};

    template <int N, int... Is>
    struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};

    template <int... Is>
    struct gen_seq<0, Is...> : index<Is...> {};
}

এবং এই জাতীয় জেনারেটর গ্রহণের ক্ষেত্রে আপনি নিজের পদ্ধতিটি প্রয়োগ করতে পারেন:

template <typename... Args, int... Is>
void func(std::tuple<Args...>& tup, helper::index<Is...>)
{
    f(std::get<Is>(tup)...);
}

template <typename... Args>
void func(std::tuple<Args...>& tup)
{
    func(tup, helper::gen_seq<sizeof...(Args)>{});
}

void act()
{
   func(args);
}

এবং এটি! সুতরাং এখন আপনার ক্লাসটি দেখতে এইরকম হওয়া উচিত:

template <typename... Ts>
class Action
{
private:
    std::function<void (Ts...)> f;
    std::tuple<Ts...> args;
public:
    template <typename F, typename... Args>
    Action(F&& func, Args&&... args)
        : f(std::forward<F>(func)),
          args(std::forward<Args>(args)...)
    {}

    template <typename... Args, int... Is>
    void func(std::tuple<Args...>& tup, helper::index<Is...>)
    {
        f(std::get<Is>(tup)...);
    }

    template <typename... Args>
    void func(std::tuple<Args...>& tup)
    {
        func(tup, helper::gen_seq<sizeof...(Args)>{});
    }

    void act()
    {
        func(args);
    }
};

কলিরুতে আপনার সম্পূর্ণ প্রোগ্রাম এখানে।


আপডেট: এখানে একটি সহায়ক পদ্ধতি যা দ্বারা টেমপ্লেট আর্গুমেন্টগুলির বিশদকরণের প্রয়োজন হয় না:

template <typename F, typename... Args>
Action<Args...> make_action(F&& f, Args&&... args)
{
    return Action<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
}

int main()
{
    auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3);

    add.act();
}

এবং আবার, এখানে অন্য ডেমো।


4
আপনি নিজের উত্তরে যে একটু প্রসারিত করতে পারেন?
এরিক বি

4
যেহেতু টিএস ... একটি শ্রেণিকালীন টেম্পলেট প্যারামিটার, কোনও ফাংশন টেম্পলেট প্যারামিটার নয়, এসএসএন্ডস ... প্যারামিটার প্যাকটির জন্য কোনও ধরণের ছাড়ের কারণ নেই বলে সর্বজনীন রেফারেন্সগুলির একটি প্যাকটি সংজ্ঞায়িত করে না। @ জোগোজাপান আপনি নির্মাতার কাছে সর্বজনীন রেফারেন্সগুলি পাস করতে পারেন তা নিশ্চিত করার সঠিক উপায়টি দেখায়।
মাস্টার্স

4
রেফারেন্স এবং অবজেক্ট লাইফটাইম জন্য সন্ধান করুন! void print(const std::string&); std::string hello(); auto act = make_action(print, hello());ভাল না। আমি আচরণকে পছন্দ std::bind, যা প্রতিটি যুক্তি একটি কপি করে তোলে, যদি না আপনি যে অক্ষম std::refবা std::cref
aschepler

4
আমি মনে করি @ জোগোজাপানের আরও একটি সংক্ষিপ্ত এবং পাঠযোগ্য সমাধান রয়েছে।
টিম কুইপার্স

4
@Riddick পরিবর্তন args(std::make_tuple(std::forward<Args>(args)...))করতে args(std::forward<Args>(args)...)। বিটিডাব্লু আমি এটি একটি দীর্ঘ সময় আগে লিখেছি এবং কিছু যুক্তি দিয়ে কোনও ফাংশন বাঁধার উদ্দেশ্যে আমি এই কোডটি ব্যবহার করব না। আমি শুধু std::invoke()বা std::apply()আজকাল ব্যবহার করব ।
0x499602D2

23

আপনি std::bind(f,args...)এই জন্য ব্যবহার করতে পারেন । এটি একটি অস্থাবর এবং সম্ভবত অনুলিপিযোগ্য বস্তু তৈরি করবে যা ফাংশন অবজেক্টের একটি অনুলিপি এবং পরবর্তী ব্যবহারের জন্য প্রতিটি আর্গুমেন্ট সংরক্ষণ করে:

#include <iostream>
#include <utility>
#include <functional>

template <typename... T>
class Action {
public:

  using bind_type = decltype(std::bind(std::declval<std::function<void(T...)>>(),std::declval<T>()...));

  template <typename... ConstrT>
  Action(std::function<void(T...)> f, ConstrT&&... args)
    : bind_(f,std::forward<ConstrT>(args)...)
  { }

  void act()
  { bind_(); }

private:
  bind_type bind_;
};

int main()
{
  Action<int,int> add([](int x, int y)
                      { std::cout << (x+y) << std::endl; },
                      3, 4);

  add.act();
  return 0;
}

লক্ষ্য করুন যে std::bindএটি একটি ফাংশন এবং আপনার এটি ডেটার সদস্য হিসাবে কল করার ফলাফল সংরক্ষণ করতে হবে। ফলাফলের ডেটা ধরণের পূর্বাভাস দেওয়া সহজ নয় (স্ট্যান্ডার্ড এমনকি এটি সুনির্দিষ্টভাবে নির্দিষ্ট করে না), তাই আমি সংকলনের সময় সেই ডেটা টাইপের সংমিশ্রণ decltypeএবং std::declvalগণনা করতে ব্যবহার করি। Action::bind_typeউপরের সংজ্ঞা দেখুন ।

এছাড়াও লক্ষ্য করুন যে আমি কীভাবে টেম্পলেটড কনস্ট্রাক্টরে সর্বজনীন রেফারেন্স ব্যবহার করেছি। এটি নিশ্চিত করে যে আপনি ক্লাস টেম্পলেট প্যারামিটারগুলির সাথে মেলে না এমন যুক্তিগুলি পাস করতে পারবেন T...(উদাহরণস্বরূপ আপনি কয়েকটিটির সাথে রুল্যু রেফারেন্স ব্যবহার করতে পারেন Tএবং আপনি তাদের bindকল হিসাবে যেমন ফরোয়ার্ড পাবেন ))

চূড়ান্ত দ্রষ্টব্য: আপনি যদি আর্গুমেন্টগুলি রেফারেন্স হিসাবে সংরক্ষণ করতে চান (যাতে আপনার পাস করা ফাংশনটি কেবল ব্যবহারের পরিবর্তে পরিবর্তিত করতে পারে) তবে আপনার std::refসেগুলি রেফারেন্স অবজেক্টে মোড়ানোর জন্য ব্যবহার করতে হবে। কেবল একটি পাস করা T &মানটির একটি অনুলিপি তৈরি করবে, কোনও রেফারেন্স নয়।

কলিরুতে অপারেশনাল কোড


মূল্যবোধ বাঁধা কি বিপজ্জনক নয়? যারা addঅন্য সুযোগে সংজ্ঞায়িত হয় তখন তাদের অকার্যকর হবে না তবে কোথায় act()ডাকা হবে? কনস্ট্রাক্টরের ConstrT&... argsচেয়ে পাওয়া উচিত নয় ConstrT&&... args?
টিম কুইপার্স

4
@ দম্পতি আমার দেরীতে জবাবের জন্য দুঃখিত। আপনি কল থেকে মূল্যবোধ বলতে চান bind()? যেহেতু bind()অনুলিপিগুলি তৈরি করার গ্যারান্টিযুক্ত (বা নতুনভাবে তৈরি বস্তুগুলিতে স্থানান্তরিত হবে), তাই আমি মনে করি কোনও সমস্যা হতে পারে।
jogojapan

@ জোগোজাপান দ্রুত দ্রষ্টব্য, এমএসভিসি 17 কনস্ট্রাক্টরের ফাংশনটি বাঁধাইতেও পাঠানো দরকার (যেমন বাঁধাই (স্ট্যান্ড :: ফরোয়ার্ড <স্ট্যান্ড :: ফাংশন <অকার্যকর (টি ...) >> (এফ), স্টাড :: ফরোয়ার্ড < ConstrT> (args) ...))
Outshined

4
ইনিশিয়ালাইজারে bind_(f, std::forward<ConstrT>(args)...)মান অনুসারে অপরিজ্ঞাত আচরণ করা হয়, যেহেতু নির্মাতা বাস্তবায়ন-সংজ্ঞায়িত। bind_typeঅনুলিপি এবং / বা স্থানান্তর-গঠনযোগ্য হিসাবে নির্দিষ্ট করা হয়েছে, যদিও bind_{std::bind(f, std::forward<ConstrT>(args)...)}এখনও কাজ করা উচিত।
joshtch

6

এই প্রশ্নটি সি ++ 11 দিনের ছিল। তবে এখন অনুসন্ধান ফলাফলগুলিতে এটি সন্ধানকারীদের জন্য কিছু আপডেট:

একজন std::tupleসদস্য এখনও আর্গুমেন্ট সাধারণত সঞ্চয় করতে সহজবোধ্য উপায়। ( @ জোগোজাপানেরstd::bind অনুরূপ সমাধান এছাড়াও কাজ করবে যদি আপনি কেবল একটি নির্দিষ্ট ফাংশন কল করতে চান তবে আপনি অন্য উপায়ে যুক্তি অ্যাক্সেস করতে চান না, বা একাধিক কার্যের পক্ষে যুক্তিগুলি পাস করতে চান ইত্যাদি না)

সি ++ ১৪ এবং তারপরে , std::make_index_sequence<N>বা std::index_sequence_for<Pack...>0x499602D2 এর সমাধানেhelper::gen_seq<N> দেখা সরঞ্জামটি প্রতিস্থাপন করতে পারে :

#include <utility>

template <typename... Ts>
class Action
{
    // ...
    template <typename... Args, std::size_t... Is>
    void func(std::tuple<Args...>& tup, std::index_sequence<Is...>)
    {
        f(std::get<Is>(tup)...);
    }

    template <typename... Args>
    void func(std::tuple<Args...>& tup)
    {
        func(tup, std::index_sequence_for<Args...>{});
    }
    // ...
};

সি ++ 17 এবং std::applyতারপরে, টিপলটি আনপ্যাকিংয়ের যত্ন নিতে ব্যবহার করা যেতে পারে:

template <typename... Ts>
class Action
{
    // ...
    void act() {
        std::apply(f, args);
    }
};

সরলিকৃত বাস্তবায়ন দেখানোর জন্য এখানে একটি সম্পূর্ণ সি ++ 17 প্রোগ্রাম রয়েছে । আমি make_actionরেফারেন্সের ধরনগুলি এড়াতে আপডেটও করেছি tuple, যা সর্বদা রুল্যু যুক্তিগুলির পক্ষে খারাপ ছিল এবং লভালু যুক্তিগুলির পক্ষে মোটামুটি ঝুঁকিপূর্ণ।


3

আমার মনে হয় আপনার একটি এক্সওয়াই সমস্যা আছে। আপনি যখন কলসাইটে কেবল একটি ল্যাম্বডা ব্যবহার করতে পারলেন তখন কেন প্যারামিটার প্যাকটি সঞ্চয় করতে সমস্ত সমস্যায় যাবেন? অর্থাত্,

#include <functional>
#include <iostream>

typedef std::function<void()> Action;

void callback(int n, const char* s) {
    std::cout << s << ": " << n << '\n';
}

int main() {
    Action a{[]{callback(13, "foo");}};
    a();
}

কারণ আমার আবেদন, একটি অ্যাকশন আসলে 3 বিভিন্ন functors যে সব সম্পর্কিত হয় আছে, এবং আমি বরং চাই শ্রেণীর এটি ধারণ 1 অ্যাকশন থাকে, এবং 3 এসটিডি :: ফাংশন
এরিক বি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.