গ্লোবাল ভেরিয়েবলের একটি রেফারেন্স থেকে ল্যাম্বডা ফাংশন পরিবর্তনীয় ক্যাপচারের আচরণগত পার্থক্য


22

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

#include <stdio.h>
#include <functional>

int n = 100;

std::function<int()> f()
{
    int &m = n;
    return [m] () mutable -> int {
        m += 123;
        return m;
    };
}

int main()
{
    int x = n;
    int y = f()();
    int z = n;

    printf("%d %d %d\n", x, y, z);
    return 0;
}

ভিএস 2015 এবং জিসিসির ফলাফল (জি ++ (উবুন্টু 5.4.0-6ubuntu1 ~ 16.04.12) 5.4.0 20160609):

100 223 100

ঝাঁকুনি ++ থেকে ফলাফল (ঝাঁকুনি সংস্করণ 3.8.0-2ubuntu4 (ট্যাগ / RELEASE_380 / চূড়ান্ত)):

100 223 223

কেন এমন হয়? এটি কি সি ++ স্ট্যান্ডার্ডের দ্বারা অনুমোদিত?


কলং এর আচরণ এখনও ট্রাঙ্কে উপস্থিত রয়েছে।
আখরোট

এগুলি সমস্ত পুরানো সংকলক সংস্করণ
এমএম ২M


1
আপনি যদি ক্যাপচারটি পুরোপুরি সরিয়ে ফেলেন, তবে জিসিসি এখনও এই কোডটি স্বীকার করে এবং ঝাঁকুনি কী করে তা করে। এটি একটি শক্তিশালী ইঙ্গিত যা একটি জিসিসি বাগ আছে - সিম্পল ক্যাপচারগুলি ল্যাম্বডা বডিটির অর্থ পরিবর্তন করার কথা নয়।
টিসি

উত্তর:


16

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

আপনার ল্যাম্বডায়, মান অনুসারে [m]ক্যাপচার m(কারণ &ক্যাপচারে কোনও নেই ), সুতরাং m(একটি রেফারেন্স হওয়া n) প্রথমে অবনমিত হয় এবং যে বিষয়টি উল্লেখ করা হয় তার একটি অনুলিপিn ক্যাপচার করা হয়। এটি করার চেয়ে এটি আলাদা নয়:

int &m = n;
int x = m; // <-- copy made!

ল্যাম্বদা তারপরে সেই অনুলিপিটি সংশোধন করে, মূলটি নয়। প্রত্যাশার মতো আপনি ভিএস এবং জিসিসি আউটপুটগুলিতে যা ঘটতে দেখছেন তা হ'ল।

ক্ল্যাং আউটপুটটি ভুল, এবং যদি এটি ইতিমধ্যে না থাকে তবে একটি বাগ হিসাবে রিপোর্ট করা উচিত।

আপনি পরিবর্তন করতে আপনার ল্যামডা চান n, ক্যাপচার mপরিবর্তে রেফারেন্স দ্বারা: [&m]। এটি অন্যটির জন্য একটি রেফারেন্স নির্ধারণের চেয়ে আলাদা নয়, যেমন:

int &m = n;
int &x = m; // <-- no copy made!

অথবা, আপনি শুধু পরিত্রাণ পেতে পারেন mপুরাপুরি এবং ক্যাপচার nরেফারেন্স দ্বারা পরিবর্তে: [&n]

যদিও, যেহেতু nবৈশ্বিক পরিসরে রয়েছে, এটি আসলেই ধরা পড়ার দরকার নেই, ল্যাম্বডা এটি ক্যাপচার না করে বিশ্বব্যাপী অ্যাক্সেস করতে পারে:

return [] () -> int {
    n += 123;
    return n;
};

5

আমি মনে করি কলঙ্ক আসলেই সঠিক হতে পারে।

মতে [lambda.capture] / 11 , একটি আইডি প্রকাশ ল্যামডা ব্যবহৃত ল্যামডা এর উপ-কপি-ব সদস্য শুধুমাত্র যদি এটি একটি গঠন বোঝায় odr ব্যবহারযোগ্য । যদি এটি না হয়, তবে এটি আসল সত্তাকে বোঝায় । এটি সি ++ 11 সাল থেকে সমস্ত সি ++ সংস্করণে প্রযোজ্য।

সি ++ 17 এর [বেসলাল.দেব.অর্ডার] / 3 অনুসারে যদি লভ্যালু-টু-রাল্যু রূপান্তরটি প্রয়োগ করে একটি ধ্রুবক অভিব্যক্তি পাওয়া যায় তবে একটি রেফারেন্স ভেরিয়েবল অদ্ভুত ব্যবহার হয় না।

C ++ 20 খসড়াতে তবে লভ্যালু-থেকে-রুল্যু রূপান্তরটির প্রয়োজনীয়তা বাদ দেওয়া হয়েছে এবং প্রাসঙ্গিক প্যাসেজটি রূপান্তরকে অন্তর্ভুক্ত বা অন্তর্ভুক্ত করতে একাধিকবার পরিবর্তিত হয়েছে। দেখুন ফিটনেসে সমস্যা 1472 এবং ফিটনেসে সমস্যা 1741 , সেইসাথে খোলা ফিটনেসে সমস্যা 2083

যেহেতু mধ্রুবক অভিব্যক্তি (স্থিতিশীল স্টোরেজ সময়কাল অবজেক্টের উল্লেখ করে) দিয়ে আরম্ভ করা হয়েছে, এটি ব্যবহার করে [expr.const] / ২.১১.১ এ ব্যতিক্রম হিসাবে ধ্রুবক প্রকাশ পাওয়া যায়

লভ্যালু-টু-রালভ্য রূপান্তরগুলি প্রয়োগ করা হয় তবে এটি ক্ষেত্রে নয়, কারণ nধ্রুবক অভিব্যক্তিতে এর মান ব্যবহারযোগ্য হয় না।

অতএব, ল্যাম্বু-এ-রালভু রূপান্তরগুলি গন্ধ-ব্যবহার নির্ধারণের জন্য প্রয়োগ করা হবে কিনা তার উপর নির্ভর করে আপনি যখন mল্যাম্বডায় ব্যবহার করেন , তখন এটি ল্যাম্বডা সদস্যকে উল্লেখ করতে পারে বা নাও পারে।

যদি রূপান্তরটি প্রয়োগ করা উচিত, জিসিসি এবং এমএসভিসি সঠিক, অন্যথায় ঝনঝন is

আপনি দেখতে পাচ্ছেন যে আপনি যদি mআর ধ্রুবক অভিব্যক্তি না হওয়ার সূচনাটি পরিবর্তন করেন তবে ক্ল্যাং এর আচরণ পরিবর্তন করে :

#include <stdio.h>
#include <functional>

int n = 100;

void g() {}

std::function<int()> f()
{
    int &m = (g(), n);
    return [m] () mutable -> int {
        m += 123;
        return m;
    };
}

int main()
{
    int x = n;
    int y = f()();
    int z = n;

    printf("%d %d %d\n", x, y, z);
    return 0;
}

এই ক্ষেত্রে সমস্ত সংকলক আউটপুট যে সম্মত হন

100 223 100

কারণ mএ ল্যামডা অবসান এর সদস্য ধরনের যা উল্লেখ করব intরেফারেন্স পরিবর্তনশীল থেকে কপি-সক্রিয়া mমধ্যে f


ভিএস / জিসিসি এবং ঝাঁকুনির ফলাফল উভয়ই সঠিক? নাকি তাদের মধ্যে একটি?
উইলি

[মৌলিক.দেব.ওডিআর] / 3 বলছে যে ভেরিয়েবলটি mঅভিজাত হিসাবে অভিহিত করে এটির নামকরণ হয় যদি না লভ্যালু-টু-রাল্যু রূপান্তরটি প্রয়োগ করা হয় তবে এটি একটি ধ্রুবক অভিব্যক্তি হবে। [Expr.const] / (২.7) এর মাধ্যমে, এই রূপান্তরটি মূল ধ্রুবক প্রকাশ নয়।
aschepler

যদি ক্ল্যাংয়ের ফলাফলটি সঠিক হয় তবে আমি মনে করি এটি কোনওরকম প্রতিদ্বন্দ্বী। কারণ প্রোগ্রামারের দৃষ্টিকোণ থেকে, তাকে নিশ্চিত হওয়া দরকার যে ক্যাপচার তালিকায় তিনি যে পরিবর্তনশীল লিখেছেন তা বাস্তবে পরিবর্তিত মামলার জন্য অনুলিপি করা হয়েছে, এবং মিটার সূচনাটি কোনও কারণে পরে প্রোগ্রামার দ্বারা পরিবর্তিত হতে পারে।
উইলি

1
m += 123;এখানে mগন্ধযুক্ত-ব্যবহৃত হয়।
অলিভ

1
আমি মনে করি বর্তমান শব্দটির দ্বারা ক্ল্যাংটি ঠিক।
টিসি

4

এটি C ++ 17 স্ট্যান্ডার্ড দ্বারা অনুমোদিত নয়, তবে এটি হতে পারে কিছু অন্যান্য স্ট্যান্ডার্ড খসড়া দ্বারা। এটি জটিল, কারণগুলির জন্য এই উত্তরে ব্যাখ্যা করা হয়নি।

[expr.prim.lambda.capture] / 10 :

অনুলিপি দ্বারা বন্দী প্রতিটি সত্তার জন্য, একটি নামহীন অ-স্থিতিশীল ডেটা সদস্য বন্ধের ধরণে ঘোষণা করা হয়। এই সদস্যদের ঘোষণার আদেশটি অনির্ধারিত। এই জাতীয় ডেটা সদস্যের প্রকারটি হ'ল রেফারেন্সড টাইপ, সত্তা যদি কোনও জিনিসের রেফারেন্স হয়, সত্তা কোনও ফাংশনের রেফারেন্স হলে রেফারেন্সড ফাংশন প্রকারের একটি লভ্যালু রেফারেন্স হয় বা অন্যথায় সংশ্লিষ্ট ক্যাপচারের সত্তার প্রকার।

[m]মানে যে পরিবর্তনশীল mমধ্যে fকপি ক্যাপচার করা হয়। সত্তাটি mঅবজেক্টের জন্য একটি রেফারেন্স, সুতরাং ক্লোজার টাইপের একটি সদস্য থাকে যার প্রকারটি রেফারেন্সড টাইপ। অর্থাত্, সদস্যের ধরণটি intএবং না int&

যেহেতু mল্যাম্বডা দেহের অভ্যন্তরের নামটি ক্লোজার অবজেক্টের সদস্যের নাম রাখে এবং এর মধ্যে পরিবর্তনশীল নয় f(এবং এটি প্রশ্নবিদ্ধ অংশ), বিবৃতিটি সেই m += 123;সদস্যটিকে সংশোধন করে, যা intথেকে পৃথক বস্তু ::n

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