"সি ++ প্রোগ্রামিং ল্যাঙ্গুয়েজ" এর এই কোডটির কি চতুর্থ সংস্করণ বিভাগ 36.3.6 এর সুসংজ্ঞায়িত আচরণ রয়েছে?


94

বাজর্ন স্ট্রাস্ট্রুপের সি ++ প্রোগ্রামিং ল্যাঙ্গুয়েজ চতুর্থ সংস্করণ বিভাগে 36.3.6 এসটিএল-মতো অপারেশনগুলিতে নিম্নলিখিত কোডটি শৃঙ্খলার উদাহরণ হিসাবে ব্যবহৃত হয় :

void f2()
{
    std::string s = "but I have heard it works even if you don't believe in it" ;
    s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
        .replace( s.find( " don't" ), 6, "" );

    assert( s == "I have heard it works only if you believe in it" ) ;
}

দৃsert়তা ব্যর্থ হয় gcc( এটি সরাসরি দেখুন ) এবং Visual Studio( এটি সরাসরি দেখুন ), তবে কলং ব্যবহার করার সময় এটি ব্যর্থ হয় না ( এটি সরাসরি দেখুন )।

আমি কেন বিভিন্ন ফলাফল পাচ্ছি? এই কম্পাইলার কোন ভুল chaining অভিব্যক্তি মূল্যায়নের করছেন বা এই কোড কিছু ফর্ম প্রদর্শন করে অনির্দিষ্ট বা অনির্ধারিত আচরণ ?


আরও ভাল:s.replace( s.replace( s.replace(0, 4, "" ).find( "even" ), 4, "only" ).find( " don't" ), 6, "" );
বেন ভয়েগট

20
বাগ একপাশে, আমি কি একমাত্র সেই মত কুৎসিত কোডটি বইয়ের মধ্যে থাকা উচিত না বলে মনে করি?
করলি হরভাথ

5
@KarolyHorvath লক্ষ্য করুন cout << a << b << coperator<<(operator<<(operator<<(cout, a), b), c)কেবল বাড়তে কম কুশ্রী হয়।
Oktalist

4
@ অক্টালিস্ট: :) কমপক্ষে আমি সেখানে অভিপ্রায়টি পেয়েছি। এটি একই সময়ে অলঙ্কার বিন্যাসে যুক্তি-নির্ভর নাম অনুসন্ধান এবং অপারেটর সিনট্যাক্স শেখায় ... এবং এটি এমন কোডটি আপনার লিখতে হবে না এমন ধারণা দেয় না।
করলি হরভাথ

উত্তর:


104

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

প্রস্তাবটি এন 4228 এ এই উদাহরণটি উল্লেখ করা হয়েছে : আইডিয়োমেটিক সি ++ এর জন্য এক্সপ্রেশন মূল্যায়ন আদেশ সংশোধন করে যা প্রশ্নের কোড সম্পর্কে নিম্নলিখিতটি বলে:

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

বিশদ

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

প্রথম নজরে এটি প্রদর্শিত হতে পারে যেহেতু প্রত্যেককে replaceবাম থেকে ডানে মূল্যায়ন করতে হয় যে সংশ্লিষ্ট ফাংশন আর্গুমেন্ট গ্রুপগুলি বাম থেকে ডানেও গ্রুপ হিসাবে মূল্যায়ন করতে হবে।

এটি ভুল, ফাংশন আর্গুমেন্টগুলির মূল্যায়নের একটি অনির্দিষ্ট অর্ডার রয়েছে যদিও চেইন ফাংশন কলগুলি প্রতিটি ফাংশন কলের জন্য একটি বাম থেকে ডানদিকে মূল্যায়ন আদেশ প্রবর্তন করে তবে প্রতিটি ফাংশন কলের আর্গুমেন্টগুলি কেবল সদস্য ফাংশন কলের সাথে সম্মতি রেখে সেগুলি কেবল ক্রমযুক্ত হয় যা তারা অংশ হয় এর বিশেষত এটি নিম্নলিখিত কলগুলিকে প্রভাবিত করে:

s.find( "even" )

এবং:

s.find( " don't" )

যেগুলি অনির্দিষ্টকালের জন্য শ্রদ্ধার সাথে ক্রমযুক্ত:

s.replace(0, 4, "" )

দুটি findকল এর আগে বা পরে মূল্যায়ন করা যেতে পারে replace, যেহেতু এটির প্রভাব যেহেতু sএমনভাবে প্রভাবিত করে যা ফলাফলকে পরিবর্তন করতে পারে find, এটি দৈর্ঘ্যের পরিবর্তন করে s। সুতরাং যখন replaceদুটি findকলের তুলনায় এটি মূল্যায়ন করা হয় তার উপর নির্ভর করে ফলাফলটি পৃথক হবে।

যদি আমরা শৃঙ্খলা প্রকাশের দিকে তাকাই এবং কিছু উপ-এক্সপ্রেশনগুলির মূল্যায়ন আদেশ পরীক্ষা করি:

s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
^ ^       ^  ^  ^    ^        ^                 ^  ^
A B       |  |  |    C        |                 |  |
          1  2  3             4                 5  6

এবং:

.replace( s.find( " don't" ), 6, "" );
 ^        ^                   ^  ^
 D        |                   |  |
          7                   8  9

নোট, আমরা সত্য যে উপেক্ষা করা হয় 4এবং 7আরও উপ-এক্সপ্রেশন বিভক্ত করা যেতে পারে। সুতরাং:

  • Aসিক্যুয়েন্সড Bযা আগে Cসিকোয়েন্সড হয় তার আগে সিক্যুয়েন্সড হয়D
  • 1থেকে 9অনিশ্চিতভাবে নীচে তালিকাভুক্ত ব্যতিক্রম কিছু সঙ্গে অন্যান্য উপ-এক্সপ্রেশন থেকে সম্মান সঙ্গে সিকোয়েন্স হয়
    • 1থেকে 3সামনে সিকোয়েন্স হয়B
    • 4থেকে 6সামনে সিকোয়েন্স হয়C
    • 7থেকে 9সামনে সিকোয়েন্স হয়D

এই ইস্যুটির মূল কথাটি হ'ল:

  • 4থেকে 9অনিশ্চিতভাবে থেকে সম্মান সঙ্গে সিকোয়েন্স হয়B

মূল্যায়নের পছন্দগুলির পক্ষে 4এবং 7সম্মানের সাথে মূল্যায়নের সম্ভাব্য ক্রম যখন মূল্যায়নের Bমধ্যে clangএবং gccকখন ফলাফলগুলির মধ্যে পার্থক্যটি ব্যাখ্যা করে f2()। আমার পরীক্ষা clangমূল্যায়ন Bমূল্যায়নের আগে 47যখন gccএটা মূল্যায়ন করেন। আমরা প্রতিটি ক্ষেত্রে কী ঘটছে তা প্রদর্শনের জন্য নিম্নলিখিত পরীক্ষার প্রোগ্রামটি ব্যবহার করতে পারি:

#include <iostream>
#include <string>

std::string::size_type my_find( std::string s, const char *cs )
{
    std::string::size_type pos = s.find( cs ) ;
    std::cout << "position " << cs << " found in complete expression: "
        << pos << std::endl ;

    return pos ;
}

int main()
{
   std::string s = "but I have heard it works even if you don't believe in it" ;
   std::string copy_s = s ;

   std::cout << "position of even before s.replace(0, 4, \"\" ): " 
         << s.find( "even" ) << std::endl ;
   std::cout << "position of  don't before s.replace(0, 4, \"\" ): " 
         << s.find( " don't" ) << std::endl << std::endl;

   copy_s.replace(0, 4, "" ) ;

   std::cout << "position of even after s.replace(0, 4, \"\" ): " 
         << copy_s.find( "even" ) << std::endl ;
   std::cout << "position of  don't after s.replace(0, 4, \"\" ): "
         << copy_s.find( " don't" ) << std::endl << std::endl;

   s.replace(0, 4, "" ).replace( my_find( s, "even" ) , 4, "only" )
        .replace( my_find( s, " don't" ), 6, "" );

   std::cout << "Result: " << s << std::endl ;
}

ফলাফল gcc( এটি সরাসরি দেখুন )

position of even before s.replace(0, 4, "" ): 26
position of  don't before s.replace(0, 4, "" ): 37

position of even after s.replace(0, 4, "" ): 22
position of  don't after s.replace(0, 4, "" ): 33

position  don't found in complete expression: 37
position even found in complete expression: 26

Result: I have heard it works evenonlyyou donieve in it

এর ফলাফল clang( এটি সরাসরি দেখুন ):

position of even before s.replace(0, 4, "" ): 26
position of  don't before s.replace(0, 4, "" ): 37

position of even after s.replace(0, 4, "" ): 22
position of  don't after s.replace(0, 4, "" ): 33

position even found in complete expression: 22
position don't found in complete expression: 33

Result: I have heard it works only if you believe in it

এর ফলাফল Visual Studio( এটি সরাসরি দেখুন ):

position of even before s.replace(0, 4, "" ): 26
position of  don't before s.replace(0, 4, "" ): 37

position of even after s.replace(0, 4, "" ): 22
position of  don't after s.replace(0, 4, "" ): 33

position  don't found in complete expression: 37
position even found in complete expression: 26
Result: I have heard it works evenonlyyou donieve in it

স্ট্যান্ডার্ড থেকে বিশদ

আমরা জানি যে সাব-এক্সপ্রেশনগুলির মূল্যায়নগুলি অসতর্কিত না করা পর্যন্ত এটি সি ++ 11 স্ট্যান্ডার্ড বিভাগ 1.9 প্রোগ্রাম এক্সিকিউশন খসড়া থেকে এসেছে যা বলে:

উল্লিখিত ব্যতীত পৃথক অপারেটরগুলির অপারেটের এবং পৃথক অভিব্যক্তির subexpresstions এর মূল্যায়ন অনির্দিষ্ট। [...]

এবং আমরা জানি যে ফাংশন কল ফাংশনটির সাথে সম্পর্কের আগে পোস্টফিক্স এক্সপ্রেশন এবং ফাংশন বডি সম্পর্কে শ্রদ্ধার সাথে যুক্তিগুলি, বিভাগ থেকে 1.9:

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

আমরা আরও জানি যে শ্রেণীর সদস্য অ্যাক্সেস এবং সেইজন্য শৃঙ্খলা বাম থেকে ডানে বাম দিক থেকে মূল্যায়ন করবে, বিভাগের 5.2.5 সদস্য অ্যাক্সেস যা বলেছেন:

[...] বিন্দু বা তীর মূল্যায়ন করার আগে পোস্টফিক্স এক্সপ্রেশন; That৪ এই মূল্যায়নের ফলাফল এবং আইডি-এক্সপ্রেশন সহ পুরো পোস্টফিক্স এক্সপ্রেশনটির ফলাফল নির্ধারণ করে।

নোট, কেস যেখানে আইডি প্রকাশ আপ একটি অ-স্ট্যাটিক সদস্য ফাংশন হচ্ছে প্রান্ত এটা মূল্যায়ন ক্রম নির্দিষ্ট করে না অভিব্যক্তি-তালিকা মধ্যে ()যেহেতু একটি পৃথক উপ-অভিব্যক্তি। 5.2 পোস্টফিক্স এক্সপ্রেশন থেকে প্রাসঙ্গিক ব্যাকরণ :

postfix-expression:
    postfix-expression ( expression-listopt)       // function call
    postfix-expression . templateopt id-expression // Class member access, ends
                                                   // up as a postfix-expression

সি ++ 17 পরিবর্তন

প্রস্তাব p0145r3: আইডিয়োমেটিক সি ++ এর জন্য এক্সপ্রেশন মূল্যায়ন আদেশকে সংশোধন করে বেশ কয়েকটি পরিবর্তন আনা হয়েছে। পোস্টফিক্স-এক্সপ্রেশন এবং তাদের এক্সপ্রেশন-তালিকার জন্য মূল্যায়নের নিয়মের ক্রমকে শক্তিশালী করে কোডটিকে সুনির্দিষ্ট আচরণের ব্যবস্থা করে এমন পরিবর্তনগুলি সহ ।

[expr.call] পি 5 বলেছেন:

পোস্টফিক্স-এক্সপ্রেশনটি এক্সপ্রেশন-তালিকার প্রতিটি অভিব্যক্তির আগে এবং কোনও ডিফল্ট আর্গুমেন্টের অনুক্রম হয় । প্রতিটি সম্পর্কিত মান গণনা এবং পার্শ্ব প্রতিক্রিয়া সহ একটি প্যারামিটারের সূচনাটি অনির্দিষ্টকালের জন্য অন্য যে কোনও প্যারামিটারের সাথে সম্মতিযুক্ত sequ [দ্রষ্টব্য: যুক্তি মূল্যায়নের সমস্ত পার্শ্ব প্রতিক্রিয়াগুলি ক্রিয়াকলাপটি প্রবেশের আগেই সিকোয়েন্সড হয় (দেখুন 4.6)) Note সংক্ষেপে নোট] [উদাহরণ:

void f() {
std::string s = "but I have heard it works even if you don’t believe in it";
s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don’t"), 6, "");
assert(s == "I have heard it works only if you believe in it"); // OK
}

পরবর্তী উদাহরণ]


7
আমি "কিছু বিশেষজ্ঞ" সমস্যাটিকে উপেক্ষা করে দেখে অবাক হয়ে কিছুটা অবাক হয়েছি, এটা ভালভাবেই জানা যায় যে কোনও ফাংশন কলের পোস্টফিক্স-এক্সপ্রেশনকে মূল্যায়ন করার পরে তর্কগুলি মূল্যায়ন করার আগে ক্রমযুক্ত হয় না (সি এবং সি ++ এর সমস্ত সংস্করণে)।
এমএম

@ শফিকিক ইয়াঘমুর ফাংশন কলগুলি অনির্দিষ্টকালের জন্য একে অপরের সাথে এবং অন্য কিছুর সাথে সম্মতভাবে সিকোয়েন্সড রয়েছে, সিকোয়েন্সড-পূর্বের সম্পর্কের ব্যতীত আপনার উল্লেখ করেছেন। যাইহোক, মূল্যায়ন 1, 2, 3, 5, 6, 8, 9, "even", "don't"এবং কয়েকটি দৃষ্টান্ত sunsequenced একে অপরের আত্মীয় হয়।
টিসি

4
@ টিটিসি না এটি নয় (যা এই "বাগ" উত্থাপন করে)। উদাহরণস্বরূপ foo().func( bar() ), এটি কল foo()করার আগে বা পরে কল করতে পারে bar()পোস্টসাফিক্স প্রকাশ হয় foo().func। আর্গুমেন্টগুলি এবং পোস্টফিক্স-এক্সপ্রেশনটি শরীরের আগে func()ক্রমযুক্ত হয় তবে একে অপরের সাথে তুলনামূলকভাবে অপরিবর্তিত।
এমএম

@ ম্যাটম্যাকনাব আহ, ঠিক আছে, আমি ভুল লিখছি। আপনি কলের চেয়ে পোস্টফিক্স-এক্সপ্রেশন সম্পর্কেই কথা বলছেন । হ্যাঁ, এটা ঠিক, এগুলি অসম্পূর্ণ (যদি না অন্য কিছু বিধি প্রয়োগ করা হয় তবে অবশ্যই)।
টিসি

6
বি স্ট্রাস্ট্রপ বইয়ে কোড উপস্থিত হওয়ার বিষয়টি কারও মনে হয় যে সঠিক কারণ অন্যথায় ইতিমধ্যে কেউ অবশ্যই লক্ষ্য করেছেন! (সম্পর্কিত; এসও ব্যবহারকারীরা কেএন্ডআর তে এখনও নতুন ভুল খুঁজে পান)
এমএম

4

এটি সি ++ 17 এর সাথে সম্পর্কিত বিষয়টিতে তথ্য যুক্ত করার উদ্দেশ্যে। উপরের কোডটি উদ্ধৃত করে ইস্যুটির সমাধানের জন্য প্রস্তাবনা ( আইডিয়োমেটিক সি ++ রিভিশন 2 এর জন্য এক্সপ্রেশন মূল্যায়ন আদেশ রিফাইং )C++17

প্রস্তাবিত হিসাবে, আমি প্রস্তাব থেকে প্রাসঙ্গিক তথ্য এবং উদ্ধৃতি (হাইলাইট খনি) যুক্ত করেছি:

অভিব্যক্তি মূল্যায়নের ক্রম, যেমন এটি বর্তমানে স্ট্যান্ডার্ডে নির্দিষ্ট করা হয়েছে, পরামর্শগুলি, জনপ্রিয় প্রোগ্রামিং আইডিয়ামগুলিকে বা স্ট্যান্ডার্ড গ্রন্থাগার সুবিধার তুলনামূলক সুরক্ষাকে হ্রাস করে। ফাঁদগুলি কেবল নবাগত বা অসতর্ক প্রোগ্রামারের জন্য নয়। তারা আমাদের সকলকে নির্বিচারে প্রভাবিত করে, এমনকি আমরা যখন বিধিগুলি জানি।

নিম্নলিখিত প্রোগ্রাম খণ্ড বিবেচনা করুন:

void f()
{
  std::string s = "but I have heard it works even if you don't believe in it"
  s.replace(0, 4, "").replace(s.find("even"), 4, "only")
      .replace(s.find(" don't"), 6, "");
  assert(s == "I have heard it works only if you believe in it");
}

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

কাগজটি C++17প্রকাশের মূল্যায়নের আদেশের পূর্ব- নিয়মটি পরিবর্তনের পরামর্শ দিয়েছিল যা Cতিন দশকেরও বেশি সময় ধরে প্রভাবিত ছিল এবং বিদ্যমান ছিল। এটি প্রস্তাব করেছিল যে ভাষাটি সমসাময়িক রীতিমতো বা " ঝুঁকিপূর্ণ ফাঁদ এবং অস্পষ্টের উত্স, বাগ খুঁজে পাওয়া শক্ত" যেমন গানের উপরের কোড নমুনার সাথে কী ঘটেছে তার গ্যারান্টি দেওয়া উচিত

প্রস্তাবটি C++17হ'ল প্রতিটি অভিব্যক্তিটির একটি সু-সংজ্ঞায়িত মূল্যায়ন আদেশ থাকতে হবে :

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

উপরের কোডটি সফলভাবে ব্যবহার করে GCC 7.1.1এবং সংকলন করে Clang 4.0.0

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