সি ++ ২০ এ পরিবর্তন ভাঙা বা ক্ল্যাঞ্জ-ট্রাঙ্ক / জিসিসি-ট্রাঙ্কে রিগ্রেশন যখন নন-বুলিয়ান রিটার্ন মানের সাথে সমতা তুলনা ওভারলোড করে?


11

নিম্নলিখিত কোডটি সি ++ 17 মোডে ক্ল্যাং-ট্রাঙ্কের সাথে জরিমানা সংকলন করে তবে সি ++ 2 এ (আসন্ন সি ++ 20) মোডে বিরতি দেয়:

// Meta struct describing the result of a comparison
struct Meta {};

struct Foo {
    Meta operator==(const Foo&) {return Meta{};}
    Meta operator!=(const Foo&) {return Meta{};}
};

int main()
{
    Meta res = (Foo{} != Foo{});
}

এটি জিসিসি-ট্রাঙ্ক বা ঝনঝন 9.0.0 এর সাথেও সূক্ষ্ম সংকলন করে: https://godbolt.org/z/8GGT78

ঝনঝন-ট্রাঙ্ক এবং ত্রুটি -std=c++2a:

<source>:12:19: error: use of overloaded operator '!=' is ambiguous (with operand types 'Foo' and 'Foo')
    Meta res = (f != g);
                ~ ^  ~
<source>:6:10: note: candidate function
    Meta operator!=(const Foo&) {return Meta{};}
         ^
<source>:5:10: note: candidate function
    Meta operator==(const Foo&) {return Meta{};}
         ^
<source>:5:10: note: candidate function (with reversed parameter order)

আমি বুঝতে পেরেছি যে সি ++ 20 কেবলমাত্র ওভারলোডের পক্ষে এটি সম্ভব operator==করবে operator!=এবং এর ফলাফলটি উপেক্ষা করে সংকলক স্বয়ংক্রিয়ভাবে উত্পন্ন হবে operator==। যতদূর আমি বুঝতে পারি, এটি কেবলমাত্র রিটার্নের টাইপ হিসাবে কাজ করে bool

সমস্যা উৎস Eigen আমরা অপারেটার একটি সেট ঘোষণা যে ==, !=, <, ... মধ্যে Arrayবস্তু বা Arrayএবং Scalars, যা রিটার্ন (একটি অভিব্যক্তি) এর একটি অ্যারের bool(যা পরে উপাদান ভিত্তিক অ্যাক্সেস করা যেতে পারে, বা অন্যথায় ব্যবহৃত )। যেমন,

#include <Eigen/Core>
int main()
{
  Eigen::ArrayXd a(10);
  a.setRandom();
  return (a != 0.0).any();
}

আমার উপরের উদাহরণের বিপরীতে এটি এমনকি জিসিসি-ট্রাঙ্কের সাথে ব্যর্থ হয়: https://godbolt.org/z/RWktKs । আমি এটিকে এখনও কোনও অ-আইগান উদাহরণে হ্রাস করতে পরিচালনা করতে পারি নি, যা ক্ল্যাং-ট্রাঙ্ক এবং জিসিসি-ট্রাঙ্ক উভয় ক্ষেত্রেই ব্যর্থ হয় (শীর্ষে উদাহরণটি বেশ সরলীকৃত)।

সম্পর্কিত ইস্যু প্রতিবেদন: https://gitlab.com/libeigen/eigen/issues/1833

আমার আসল প্রশ্ন: এটি কি আসলেই সি ++ ২০ এর একটি ব্রেকিং পরিবর্তন (এবং মেটা-অবজেক্টগুলি ফেরত দিতে তুলনামূলক অপারেটরদের ওভারলোড করার কোনও সম্ভাবনা রয়েছে), বা ঝড় / জিসিসি-তে এটি সম্ভবত রিগ্রেশন হওয়ার সম্ভাবনা বেশি?


সম্পর্কিত: stackoverflow.com/questions/58319928/...
chtz

উত্তর:


5

আইগেন ইস্যুটি নিম্নলিখিতটিতে হ্রাস পেয়েছে:

using Scalar = double;

template<class Derived>
struct Base {
    friend inline int operator==(const Scalar&, const Derived&) { return 1; }
    int operator!=(const Scalar&) const;
};

struct X : Base<X> {};

int main() {
    X{} != 0.0;
}

দুই প্রার্থী হলেন মতপ্রকাশের জন্য

  1. পুনরায় লিখিত প্রার্থী থেকে operator==(const Scalar&, const Derived&)
  2. Base<X>::operator!=(const Scalar&) const

প্রতি [over.match.funcs] / 4 , যেমন কোনও ব্যবহার-ঘোষণার দ্বারা স্কোপটিতেoperator!= আমদানি করা হয়নি , # 2 এর জন্য অন্তর্নিহিত অবজেক্ট প্যারামিটারের ধরণ । ফলস্বরূপ, # 1 এর পক্ষে সেই আর্গুমেন্টের জন্য আরও ভাল অন্তর্নিহিত রূপান্তর সিকোয়েন্স রয়েছে (সঠিক মিল, পরিবর্তিত পরিবর্তনের চেয়ে বেস রূপান্তর চেয়ে)। # 1 নির্বাচন করা পরে প্রোগ্রামটিকে খারাপভাবে রেন্ডার করে।Xconst Base<X>&

সম্ভাব্য সংশোধনসমূহ:

  • যোগ using Base::operator!=;করুন Derived, বা
  • এর পরিবর্তে operator==একটি নিতে পরিবর্তন করুন ।const Base&const Derived&

আসল কোডটি boolতাদের থেকে কোনও ফিরে আসতে পারে না এমন কোনও কারণ আছে কি operator==? কারণ এটি মনে করা হয় যে নতুন নিয়মগুলির অধীনেই কোডটি খারাপভাবে গঠন করা হয়েছে।
নিকোল বোলাস

4
আসল কোডটিতে এমন একটি থাকে operator==(Array, Scalar)যা উপাদান অনুসারে তুলনা করে এবং এর সাথে ফিরে Arrayআসে bool। আপনি সমস্ত কিছু না boolভেঙে এটিকে রূপান্তর করতে পারবেন না ।
টিসি

2
এটি স্ট্যান্ডার্ডের কোনও ত্রুটির মতো মনে হচ্ছে। পুনর্লিখনের নিয়মগুলি operator==বিদ্যমান কোডকে প্রভাবিত করার কথা ছিল না, তবুও তারা এই ক্ষেত্রে তা করে, কারণ কোনও boolফেরতের মূল্যের জন্য চেক পুনর্লিখনের জন্য প্রার্থী বাছাইয়ের অংশ নয়।
নিকল বোলাস

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

বাহ, অনেক ধন্যবাদ, আমি অনুমান করি আপনার সমাধানটি আমাদের সমস্যাটি সমাধান করবে (মুহূর্তে যুক্তিসঙ্গত প্রচেষ্টার সাথে আমার কাছে জিসিসি / ক্ল্যাং ট্রাঙ্ক ইনস্টল করার সময় নেই, তাই আমি কেবলমাত্র চেক করব যে এটি সর্বশেষ স্থিতিশীল সংকলনের সংস্করণগুলিতে কিছু ভেঙে গেছে কিনা? )।
chtz

11

হ্যাঁ, কোডটি আসলে সি ++ 20 এ বিভক্ত।

অভিব্যক্তিটির Foo{} != Foo{}সি ++ ২০ তে তিন জন প্রার্থী রয়েছে (যেখানে সি ++ 17 তে কেবল একজন ছিলেন):

Meta operator!=(Foo& /*this*/, const Foo&); // #1
Meta operator==(Foo& /*this*/, const Foo&); // #2
Meta operator==(const Foo&, Foo& /*this*/); // #3 - which is #2 reversed

এটি [over.match.oper] /3.4-র নতুন লিখিত প্রার্থীদের বিধি থেকে আসে । ঐ প্রার্থীর সকল টেকসই হয়, যেহেতু আমাদের আর্গুমেন্ট নয় । সেরা व्यवहारযোগ্য প্রার্থী খুঁজে পেতে, আমাদের টাইব্রেকারগুলির মধ্য দিয়ে যেতে হবে।Fooconst

সেরা ব্যবহারযোগ্য ফাংশনের জন্য প্রাসঙ্গিক বিধিগুলি [ওভার.ম্যাচ.বেস্ট] / ২ থেকে :

এই সংজ্ঞাগুলি দেওয়া, একটি কার্যক্ষম F1ফাংশনটিকে অন্য কার্যক্ষম ফাংশনের চেয়ে আরও ভাল ফাংশন হিসাবে সংজ্ঞায়িত করা হয় F2যদি সমস্ত যুক্তির জন্য i, এর চেয়ে খারাপ রূপান্তর ক্রম নয় এবং তারপরে ICSi(F1)ICSi(F2)

  • [... এই উদাহরণের জন্য প্রচুর অপ্রাসঙ্গিক কেস ...] বা, যদি তা না হয় তবে
  • এফ 2 হ'ল একটি লিখিত প্রার্থী ([over.match.oper]) এবং F1 নয়
  • এফ 1 এবং এফ 2 হ'ল পুনর্লিখিত প্রার্থী, এবং এফ 2 প্যারামিটারগুলির বিপরীত ক্রম সহ সংশ্লেষযুক্ত প্রার্থী এবং এফ 1 নয়

#2এবং #3পুনরায় লিখিত প্রার্থী, এবং #3পরামিতিগুলির ক্রমগুলি বিপরীত করেছে, যদিও #1নতুন করে লেখা হয়নি। তবে সেই টাইব্রেকারে উঠতে আমাদের প্রথমে সেই প্রাথমিক শর্তটি পাওয়া উচিত: সমস্ত যুক্তির জন্য রূপান্তর ক্রমগুলি আরও খারাপ নয়।

#1বেশী ভালো #2(কারণ ফাংশন পরামিতি একই জাভাস্ক্রিপ্টে গার্বেজ,) কারণ সব রূপান্তর সিকোয়েন্স একই এবং #2একটি পুনর্লিখিত প্রার্থী থাকাকালীন #1নয়।

তবে ... উভয় জোড়া #1/ #3এবং #2/ #3 প্রথম অবস্থাতে আটকে যায়। উভয় ক্ষেত্রেই, প্রথম প্যারামিটারের #1/ এর জন্য আরও ভাল রূপান্তর সিকোয়েন্স থাকে #2যখন দ্বিতীয় প্যারামিটারের জন্য আরও ভাল রূপান্তর সিকোয়েন্স থাকে #3(যে প্যারামিটারটিতে constঅতিরিক্ত constযোগ্যতা অর্জন করতে হয়, তাই এটির আরও খারাপ রূপান্তর ক্রম রয়েছে)। এই constফ্লিপ-ফ্লপ আমাদের কোনও একটিকে পছন্দ করতে না সক্ষম করে তোলে।

ফলস্বরূপ, পুরো ওভারলোড রেজোলিউশন দ্বিধাগ্রস্ত।

যতদূর আমি বুঝতে পারি, এটি কেবলমাত্র রিটার্নের টাইপ হিসাবে কাজ করে bool

এটি সঠিক নয়। আমরা নিঃশর্তভাবে পুনর্লিখিত এবং বিপরীত প্রার্থীদের বিবেচনা করি। আমাদের নিয়মটি [over.match.oper] / 9 থেকে :

operator==অপারেটরের জন্য ওভারলোড রেজোলিউশনের মাধ্যমে যদি কোনও পুনরায় লিখিত প্রার্থীকে নির্বাচিত করা হয় তবে @তার রিটার্নের ধরনটি সিভি হবে bool

যে, আমরা এখনও এই প্রার্থীদের বিবেচনা করি। তবে যদি সর্বোত্তম ব্যবহারযোগ্য প্রার্থী যদি কোনও operator==প্রত্যাবর্তন করে তবে বলে নিন Meta- ফলাফলটি মূলত একইরকম, যেন সেই প্রার্থী মোছা হয়েছিল।

আমরা কি না একটি রাষ্ট্র হতে জমিদার রেজল্যুশন রিটার্ন টাইপ বিবেচনা করতে হবে যেখানে চাই। এবং যে কোনও ক্ষেত্রে, এখানে কোডটি যে ফেরত দেয় Metaতা অবিচ্ছেদ্য - যদি এটি ফিরে আসে তবে সমস্যাটিও উপস্থিত থাকবে bool


ধন্যবাদ, এখানে ঠিক করা সহজ:

struct Foo {
    Meta operator==(const Foo&) const;
    Meta operator!=(const Foo&) const;
    //                         ^^^^^^
};

একবার আপনি উভয় তুলনামূলক অপারেটর তৈরি করলে const, আর কোনও দ্বিধা নেই is সমস্ত প্যারামিটার একই, সুতরাং সমস্ত রূপান্তর ক্রম তুচ্ছভাবে একই। #1এখন #3পুনর্লিখন দ্বারা নয় এবং #2এখন #3বিপরীত না হয়ে পরাজিত করবে - যা #1সর্বোত্তম কার্যকর প্রার্থী করে। আমাদের সি ++ 17 তে একই ফলাফল, সেখানে পৌঁছানোর জন্য আরও কয়েকটি ধাপ।


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

রিটার্ন টাইপ এমন কোনও কিছু যা অপারেটরকে সমর্থন করে না! এটির পক্ষে আসলে
দুর্গন্ধযুক্ত

1
@ ক্রিসডড না, এটি হুবহু হতে হবে cv bool(এবং এই পরিবর্তনের আগে প্রয়োজনটি প্রাসঙ্গিক রূপান্তর ছিল bool- এখনও নয় !)
ব্যারি

দুর্ভাগ্যক্রমে, এটি আমার আসল সমস্যাটির সমাধান করে না, তবে এটি ছিল কারণ আমি এমন একটি এমআরই সরবরাহ করতে ব্যর্থ হয়েছিল যা আমার সমস্যাটিকে প্রকৃতপক্ষে বর্ণনা করে। আমি এটি গ্রহণ করব এবং যখন আমি আমার সমস্যাটি সঠিকভাবে হ্রাস করতে সক্ষম হব তখন আমি একটি নতুন প্রশ্ন জিজ্ঞাসা করব ...
chtz

2
দেখে মনে হচ্ছে মূল ইস্যুটির যথাযথ হ্রাস হ'ল gcc.godbolt.org/z/tFy4qz
টিসি

5

[over.match.best] / 2 তালিকাতে কীভাবে কোনও সেটে বৈধ ওভারলোডগুলি অগ্রাধিকার দেওয়া হয় তা তালিকাভুক্ত করে। বিভাগ ২.৮ আমাদের জানায় যে ( অন্যান্য অনেক কিছুর মধ্যে) এর F1চেয়ে ভাল :F2

F2একজন পুনর্লিখিত প্রার্থী ([over.match.oper]) এবং F1নেই

উদাহরণস্বরূপ সেখানে উপস্থিত operator<থাকা সত্ত্বেও একটি স্পষ্ট বলা হচ্ছে operator<=>

এবং [over.match.oper] /3.4.3 আমাদের বলছে যে operator==এই পরিস্থিতিতে প্রার্থিতা হ'ল একটি লিখিত প্রার্থী।

তবে , আপনার অপারেটররা একটি গুরুত্বপূর্ণ বিষয় ভুলে যায়: সেগুলি constকার্যকরী হওয়া উচিত । এবং এগুলি না constকরে ওভারলোড রেজোলিউশনের পূর্ববর্তী দিকগুলি কার্যকর হয় না। যেমন অ আমরাও ফাংশন, একটি সঠিক মিল IS const-to- constধর্মান্তর বিভিন্ন আর্গুমেন্ট ঘটতে করতে হবে। এটি প্রশ্নে অস্পষ্টতা সৃষ্টি করে।

একবার এগুলি তৈরি করার পরে const, ক্ল্যাং ট্রাঙ্ক সংকলন করে

আমি কোডটি জানি না বলে আমি বাকি ইগেনের সাথে কথা বলতে পারি না, এটি অনেক বড়, এবং এটি এমসিসিই-তে ফিট করতে পারে না।


2
সমস্ত আর্গুমেন্টের জন্য যদি সমানভাবে ভাল রূপান্তর থাকে তবে আমরা কেবলমাত্র আপনার তালিকাভুক্ত টাইব্রেকারে উঠি। তবে এখানে নেই: নিখোঁজ হওয়ার কারণে const, বিপরীত প্রার্থীদের দ্বিতীয় আর্গুমেন্টের জন্য আরও ভাল রূপান্তর ক্রম রয়েছে এবং বিপরীত প্রার্থীদের প্রথম যুক্তির জন্য আরও ভাল রূপান্তর ক্রম রয়েছে।
রিচার্ড স্মিথ

@ রিচার্ডস্মিথ: হ্যাঁ, এটাই আমি বলছিলাম এমন জটিলতা। তবে আমি আসলে এই নিয়মগুলি পড়তে এবং অভ্যন্তরীণ করতে চাইনি;)
নিকল বোলাস

প্রকৃতপক্ষে, আমি constসর্বনিম্ন উদাহরণে ভুলে গেছি । আমি বেশ নিশ্চিত যে আইগেন constসর্বত্র ব্যবহার করে (বা শ্রেণীর সংজ্ঞাগুলির বাইরেও, constউল্লেখ সহ), তবে আমার চেক করা দরকার। আমি যখন সময় পাই তখন ইগেন সর্বনিম্ন উদাহরণ হিসাবে ব্যবহার করে সামগ্রিক প্রক্রিয়াটি ভেঙে ফেলার চেষ্টা করি।
chtz

-1

আমাদের গোপ্যাক্স শিরোলেখ ফাইলগুলির সাথে আমাদের একই সমস্যা রয়েছে। ক্ল্যাং -10 এবং -এসটিডি = সি ++ 2 এ দিয়ে নিম্নলিখিতগুলি সংকলন একটি সংকলক ত্রুটি তৈরি করে।

template<typename T> class gpu_type;

using gpu_bool     = gpu_type<bool>;
using gpu_int      = gpu_type<int>;

template<typename T>
class gpu_type
{
  friend inline gpu_bool operator==(T a, const gpu_type& b);
  friend inline gpu_bool operator!=(T a, const gpu_type& b);
};

int main()
{
  gpu_int a;
  gpu_bool b = (a == 0);
}

এই অতিরিক্ত অপারেটর সরবরাহ করা সমস্যার সমাধান বলে মনে হচ্ছে:

template<typename T>
class gpu_type
{
  ...
  friend inline gpu_bool operator==(const gpu_type& b, T a);
  friend inline gpu_bool operator!=(const gpu_type& b, T a);
};

1
এটি কি এমন কিছু ছিল না যা আগে ব্যবহার করতে কার্যকর হবে? নাহলে কীভাবে a == 0সংকলন হত ?
নিকোল বোলাস

এটি আসলে একটি অনুরূপ সমস্যা নয়। নিকোল যেমন উল্লেখ করেছেন, এটি ইতিমধ্যে C ++ 17 তে সংকলন করে নি। এটি কেবলমাত্র অন্য কারণে, সি ++ ২০ এ সংকলন না করে চলে।
ব্যারি

আমি উল্লেখ করতে ভুলে গেছি: আমরা সদস্য অপারেটরগুলিও সরবরাহ করি: gpu_bool gpu_type<T>::operator==(T a) const;এবং gpu_bool gpu_type<T>::operator!=(T a) const;সি ++ - 17 দিয়ে, এটি দুর্দান্ত কাজ করে। তবে এখন ঝনঝন -10 এবং সি ++ - 20 এর সাথে এগুলি আর খুঁজে পাওয়া যায় না এবং পরিবর্তে সংকলকটি আর্গুমেন্টগুলি অদলবদল করে নিজের অপারেটরগুলি তৈরি করার চেষ্টা করে এবং এটি ব্যর্থ হয়, কারণ রিটার্নের ধরণটি নেই bool
ইনগো জোসপেইট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.