কনস্ট রেফারেন্স হিসাবে লাম্বদা ক্যাপচার?


166

লাম্বডা অভিব্যক্তিতে কনস্ট্যান্ট রেফারেন্স দ্বারা ক্যাপচার করা কি সম্ভব?

আমি নীচে চিহ্নিত অ্যাসাইনমেন্টটি ব্যর্থ হতে চাই, উদাহরণস্বরূপ:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
    return 0;
}

আপডেট: এটি যেহেতু একটি পুরানো প্রশ্ন, এটির সাহায্যের জন্য সি ++ 14 এ সুবিধা থাকলে এটি আপডেট করা ভাল। সি ++ 14 এর এক্সটেনশানগুলি কি কনস্ট্যান্ট রেফারেন্স দ্বারা একটি নন-কনস্ট্যান্ট অবজেক্টটি ক্যাপচার করার অনুমতি দেয়? ( আগস্ট 2015 )


: মত আপনার ল্যামডা বর্ণন করা উচিত নয় [&, &best_string](string const s) { ...}?
এরজোট

3
সত্যিই বেমানান ক্যাপচার। "কনস্ট্যান্ড" খুব কার্যকর হতে পারে যখন আপনার কাছে বড় কনস্ট অবজেক্ট থাকে যা ল্যাম্বডা ফাংশনে অ্যাক্সেস করা উচিত তবে সংশোধন করা উচিত নয়
সার্জটেক

কোড তাকান। আপনি দুটি পরামিতি ল্যাম্বদা ব্যবহার করতে পারেন এবং দ্বিতীয়টিকে কনস্টের রেফারেন্স হিসাবে আবদ্ধ করতে পারেন। যদিও একটি ব্যয় সঙ্গে আসে।
অ্যালেক্স

1
এটি সি ++ 11 এ সম্ভব বলে মনে হচ্ছে না। তবে সম্ভবত আমরা এই প্রশ্নটিকে C ++ 14 এর জন্য আপডেট করতে পারি - এমন কোনও এক্সটেনশান রয়েছে যা এটির অনুমতি দেয়? সি ++ ১৪ জেনারালাইজড ল্যাম্বদা ক্যাপচার করে?
অ্যারন ম্যাকডেইড

উত্তর:


127

const n3092 হিসাবে ক্যাপচারের জন্য ব্যাকরণে নেই:

capture:
  identifier
  & identifier
  this

পাঠ্যটিতে কেবল ক্যাপচার বাই কপি এবং ক্যাপচার বাই রেফারেন্স উল্লেখ করা হয়েছে এবং কোনও ধরণের কনস্ট-নেসের কথা উল্লেখ করা হয়নি mention

আমার কাছে একটি তদারকির মতো মনে হয় তবে আমি খুব কাছ থেকে মানক প্রক্রিয়াটি অনুসরণ করি নি।


47
আমি ক্যাপচারটি পরিবর্তনযোগ্য যা পরিবর্তনযোগ্য হয়েছিল তা পরিবর্তন করে আবার একটি ভেরিয়েবল ফিরে পেয়েছি, কিন্তু হওয়া উচিত ছিল const। বা আরও সঠিকভাবে, ক্যাপচার ভেরিয়েবলটি থাকলে const, সংকলক প্রোগ্রামারটিতে সঠিক আচরণ প্রয়োগ করত। সিনট্যাক্স সমর্থিত হলে এটি দুর্দান্ত হবে [&mutableVar, const &constVar]
শন

দেখে মনে হচ্ছে এটি সি ++ 14 দিয়ে হওয়া সম্ভব, তবে আমি এটি কাজ করতে পারি না। কোনও পরামর্শ?
অ্যারন ম্যাকডেইড

37
দৃ captured়তা বন্দী ভেরিয়েবল থেকে উত্তরাধিকার সূত্রে প্রাপ্ত। তাই আপনি যদি ক্যাপচার করতে চান aযেমন const, ঘোষণা const auto &b = a;ল্যামডা এবং ক্যাপচার সামনেb
StenSoft

7
পুনঃটুইট স্পষ্টতই রেফারেন্স অনুসারে সদস্য-পরিবর্তনশীল ক্যাপচার করার সময় এটি প্রয়োগ হয় না: [&foo = this->foo]কোনও constফাংশনের অভ্যন্তরে আমাকে একটি ত্রুটি দেয় যা উল্লেখ করে যে ক্যাপচারটি নিজেই বাছাইপর্বকে বাতিল করে দেয়। এটি জিসিসি 5.1 এ একটি বাগ হতে পারে, যদিও আমি মনে করি।
কাইল স্ট্র্যান্ড

119

ভিতরে ব্যবহার static_cast/ const_cast:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

ডেমো


ভিতরে ব্যবহার std::as_const:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

ডেমো 2


এছাড়াও, সম্ভবত এটি গ্রহণযোগ্য উত্তরে সম্পাদনা করা উচিত? যেভাবেই হোক না কেন, একটি ভাল উত্তর থাকতে হবে যা সি ++ 11 এবং সি ++ 14 উভয়ই জুড়ে। যদিও, আমি অনুমান করি যে এটি যুক্তিযুক্ত হতে পারে যে সি ++ ১৪ আগামী বছরগুলিতে প্রত্যেকের পক্ষে যথেষ্ট ভাল হবে
অ্যারন ম্যাকডেইড

12
অ্যারোনম্যাকডেড const_castনিঃশর্তে কোনও অস্থির বস্তুটি কোনও কনস্ট কনটেক্টে (যখন constবলার জন্য বলা হয় ) পরিবর্তন করতে পারে , এইভাবে, আমার পছন্দসই বাধা যুক্ত করার জন্যstatic_cast
পাইোটার স্কটনিকিকে

1
অন্যদিকে @ পাইটরসকোটনিকি, static_castরেফারেন্সটি উল্লেখ করতে নিঃশব্দে একটি অস্থায়ী তৈরি করতে পারে যদি আপনি ঠিক মতো টাইপ না পান
এমএম

24
@MM &basic_string = std::as_const(best_string)সকল সমস্যার সমাধান করা উচিত
পাযত্র Skotnicki

14
@PiotrSkotnicki যে কিছু লিখতে যে একটি বীভত্স ভাবে হচ্ছে সমস্যা ছাড়া উচিত হিসাবে সহজ হতে const& best_string
কাইল স্ট্র্যান্ড

12

আমি মনে করি ক্যাপচার অংশটি নির্দিষ্ট করা উচিত নয় const, যেমন ক্যাপচারটির অর্থ এটি কেবল বাহ্যিক স্কোপ ভেরিয়েবল অ্যাক্সেস করার একটি উপায় প্রয়োজন।

স্পেসিফায়ার বাহ্যিক স্কোপে আরও ভাল নির্দিষ্ট করা হয় specified

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

ল্যাম্বদা ফাংশনটি কনস্ট (তার স্কোপটিতে মান পরিবর্তন করতে পারে না), সুতরাং যখন আপনি মান অনুসারে ভেরিয়েবল ক্যাপচার করবেন তখন ভেরিয়েবলটি পরিবর্তন করা যায় না, তবে রেফারেন্স লাম্বদা স্কোপে নেই।


1
@ অমরনাথ বালাসুব্রমানি: এটি আমার মতে, আমি মনে করি লাম্বদা ক্যাপচার অংশে কোনও কনস্টের রেফারেন্স নির্দিষ্ট করার দরকার নেই, কেন এখানে ভেরিয়েবল কনস্ট থাকতে হবে এবং অন্য জায়গায় কনস্ট করা উচিত নয় (যদি সম্ভব হয় তবে এটি ত্রুটিযুক্ত হবে) )। যাইহোক আপনার প্রতিক্রিয়া দেখে খুশি।
zhb

2
যদি আপনার ধারণক্ষেত্রটির better_stringমধ্যে পরিবর্তন করতে হয় তবে এই সমাধানটি কার্যকর হবে না work কনস্ট-রেফ হিসাবে ক্যাপচারের জন্য ব্যবহারের ক্ষেত্রে হ'ল যখন ভেরিয়েবলটি ল্যাপডের মধ্যে নয় তবে ধারণযোগ্য স্কোপটিতে পরিবর্তনের প্রয়োজন।
জোনাথন শারমান

@ জোনাথনশর্মণ, কোনও ভেরিয়েবলের কাছে কনস্টের রেফারেন্স তৈরি করতে আপনার কোনও ব্যয় হয় না, তাই আপনি এটি করতে const string &c_better_string = better_string;এবং খুশিতে ল্যাম্বডায় এটি দিতে পারেন:[&c_better_string]
স্টেড

@ স্টেইড এটির সমস্যাটি হ'ল আপনি আশেপাশের সুযোগে অতিরিক্ত পরিবর্তনশীল নাম প্রবর্তন করছেন। আমি মনে করি উপরের পাইওটার স্কটনিকির সমাধানটি সবচেয়ে পরিষ্কার, কারণ এটি পরিবর্তনশীল স্কোপগুলি ন্যূনতম রাখার সাথে সাথে কনস্ট্যান্ট-যথার্থতা অর্জন করে।
জোনাথন শারম্যান

@ জোনাথনশর্মণ, এখানে আমরা মতামতের জমিতে প্রবেশ করি - সবচেয়ে সুন্দর বা পরিষ্কার, বা যা কিছু। আমার বক্তব্যটি হ'ল উভয় সমাধানই কাজের জন্য উপযুক্ত।
স্টিড

8

আমার ধারণা আপনি যদি ভেরিয়েবলটিকে ফান্টারের প্যারামিটার হিসাবে ব্যবহার না করে থাকেন তবে আপনার বর্তমান ফাংশনের অ্যাক্সেস স্তরটি ব্যবহার করা উচিত। যদি আপনি ভাবেন যে আপনার উচিত নয়, তবে আপনার ল্যাম্বডাকে এই ফাংশন থেকে আলাদা করুন, এটি এর অংশ নয়।

যাইহোক, পরিবর্তে অন্য কনস্ট রেফারেন্স ব্যবহার করে আপনি যা চান একই জিনিসটি সহজেই অর্জন করতে পারেন:

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

তবে এটি একইভাবে ধরে নেওয়া সমান যে আপনার ল্যাম্বডাকে বর্তমান ফাংশন থেকে আলাদা করতে হবে, এটি একটি ল্যাম্বডা তৈরি করবে।


1
ক্যাপচারের ধারাটিতে এখনও কেবল উল্লেখ করা হয়েছে best_string। এগুলি ছাড়াও, জিসিসি 4.5 কোডটি সফলভাবে কোডটিকে "সফলভাবে প্রত্যাখ্যান করে"।
22it

হ্যাঁ, এটি আমার ফলাফলগুলি প্রযুক্তিগত স্তরে অর্জন করার চেষ্টা করছিলাম give শেষ পর্যন্ত তবে, আমার আসল প্রশ্নের উত্তরটি "না" বলে মনে হচ্ছে।
জন ডিবলিং

কেন এটি এটিকে "নন-ল্যাম্বডা" করে তুলবে?

কারণ একটি ল্যাম্বডা প্রকৃতি এটি প্রাসঙ্গিক নির্ভর dependent আপনার যদি কোনও নির্দিষ্ট প্রসঙ্গের প্রয়োজন না হয় তবে এটি একটি ফান্টিকার তৈরির কেবল দ্রুত উপায়। যদি ফান্টারের প্রসঙ্গ-স্বতন্ত্র হওয়া উচিত তবে এটিকে আসল ফ্যাক্টর করুন।
ক্লাইম

3
"যদি ফান্টারের প্রসঙ্গ-স্বতন্ত্র হওয়া উচিত তবে এটিকে আসল ফ্যাক্টর করুন" ... এবং সম্ভাব্য বিদায় বানাতে চুম্বন করা উচিত?
অ্যান্ড্রু লাজার

5

আমার মনে হয় আপনার কাছে তিনটি আলাদা বিকল্প রয়েছে:

  • কনস্ট্যান্ড রেফারেন্স ব্যবহার করবেন না, তবে একটি অনুলিপি ক্যাপচার ব্যবহার করুন
  • এটি পরিবর্তনশীল যে সত্য তা উপেক্ষা করুন
  • বাইনারি ফাংশনটির একটি যুক্তিকে বাঁধতে std :: bind ব্যবহার করুন যার কনস্ট রেফারেন্স রয়েছে।

একটি অনুলিপি ব্যবহার

অনুলিপি ক্যাপচারগুলির সাথে ল্যাম্বডাস সম্পর্কে আকর্ষণীয় অংশটি হ'ল সেগুলি কেবলমাত্র পঠিত এবং তাই আপনি যা চান তা ঠিক তাই করুন।

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

std :: bind ব্যবহার করে

std::bindএকটি ফাংশন এর শালীনতা হ্রাস। তবে মনে রাখবেন যে এটি / ফাংশন পয়েন্টারের মাধ্যমে একটি পরোক্ষ ফাংশন কলকে সরিয়ে দেয়।

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}

1
ধারণক্ষেত্রের স্কোয়ারের পরিবর্তনশীলগুলি বাদ দেওয়া ল্যাম্বডায় প্রতিফলিত হবে না। এটি কোনও রেফারেন্স নয়, এটি কেবল একটি পরিবর্তনশীল যা পুনরায় নিয়োগ করা উচিত নয় কারণ পুনরায় নিয়োগের অর্থ যা বোঝায় তা বোঝায় না।
গ্রেট

4

একটি ছোট উপায় আছে।

মনে রাখবেন যে "Best_string" এর আগে কোনও অ্যাম্পারস্যান্ড নেই।

এটি "কনস্ট্যান্ড স্টাড :: রেফারেন্স_ওয়ার্প << টি >>" টাইপের হবে।

[best_string = cref(best_string)](const string& s)
{
    best_string = s; // fails
};

http://coliru.stacked-crooked.com/a/0e54d6f9441e6867


0

এই জিসিসি বাগটি স্থির না হওয়া পর্যন্ত ঝাঁকুনি ব্যবহার করুন বা অপেক্ষা করুন: বাগ 70385: কনস্টের রেফারেন্সের সাহায্যে লাম্বদা ক্যাপচার ব্যর্থ হয় [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]


1
যদিও এই লিঙ্কটি প্রশ্নের উত্তর দিতে পারে, উত্তরের প্রয়োজনীয় অংশগুলি এখানে অন্তর্ভুক্ত করা এবং রেফারেন্সের জন্য লিঙ্কটি সরবরাহ করা ভাল। লিঙ্কযুক্ত পৃষ্ঠাগুলি পরিবর্তিত হলে কেবল লিঙ্ক-উত্তরই অবৈধ হয়ে উঠতে পারে। "
Div

ঠিক আছে, আমি এখানে আমার উত্তরটি সম্পাদিত করেছি জিসিসি বাগের বিবরণ যুক্ত করতে।
ব্যবহারকারী 1448926

এটি কোনও পরোক্ষভাবে প্রশ্নের উত্তর, যদি থাকে। বাগটি কোনও কনস্টোন ক্যাপচার করার সময় সংকলকটি কীভাবে ব্যর্থ হয় সে সম্পর্কে রয়েছে, সুতরাং কেন সম্ভবত প্রশ্নটিতে সমস্যাটি সমাধান করার বা কাজ করার কোনও উপায় জিসিসি দিয়ে কাজ করতে পারে না।
স্টেইন

0

কনস্টের সাহায্যে অ্যালগরিদম এম্পারস্যান্ডটি স্ট্রিংটিকে মূল মূল্যে সেট করে দেয়, অন্য কথায়, ল্যাম্বডা সত্যিই নিজেকে ফাংশনটির প্যারামিটার হিসাবে সংজ্ঞায়িত করবে না, যদিও পার্শ্ববর্তী স্কোপটিতে অতিরিক্ত পরিবর্তনশীল থাকবে ... এটি সংজ্ঞায়িত না করে তবে, এটি স্ট্রিংটিকে সাধারণত [&, & best_string] (স্ট্রিং কনস্ট) হিসাবে সংজ্ঞায়িত করে না , সুতরাং , সম্ভবত আমরা রেফারেন্সটি ক্যাপচার করার চেষ্টা করে এটিকে ছেড়ে দিলে এটির পক্ষে সম্ভবত আরও ভাল।


এটি একটি অতি পুরানো প্রশ্ন: আপনার উত্তরে আপনি কোন সি ++ সংস্করণ উল্লেখ করছেন সে সম্পর্কিত প্রসঙ্গের অভাব রয়েছে। দয়া করে এই বিষয়বস্তু সরবরাহ করুন।
ZF007
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.