কেন সি ++ টি <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


9

নিম্নলিখিত টেমপ্লেট কাঠামো দেওয়া হয়েছে:

template<typename T>
struct Foo {
    Foo(T&&) {}
};

এটি সংকলন করে এবং Tহ্রাস করা হয় int:

auto f = Foo(2);

তবে এটি সংকলন করে না: https://godbolt.org/z/hAA9TE

int x = 2;
auto f = Foo(x);

/*
<source>:12:15: error: no viable constructor or deduction guide for deduction of template arguments of 'Foo'
    auto f = Foo(x);
             ^

<source>:7:5: note: candidate function [with T = int] not viable: no known conversion from 'int' to 'int &&' for 1st argument
    Foo(T&&) {}
    ^
*/

তবে, Foo<int&>(x)গ্রহণ করা হয়।

তবে আমি যখন আপাতদৃষ্টিতে অপ্রয়োজনীয় ব্যবহারকারী-সংজ্ঞায়িত ছাড়ের গাইড যোগ করি তখন এটি কাজ করে:

template<typename T>
Foo(T&&) -> Foo<T>;

কোনও ব্যবহারকারী-সংজ্ঞায়িত ছাড়ের গাইড ছাড়া Tহিসাবে কেনা যাবে না int&?



এই প্রশ্নটি টেমপ্লেট প্রকারের মতো বলে মনে হচ্ছেFoo<T<A>>
জেটব্যান্ডস

উত্তর:


5

আমি মনে করি এখানে বিভ্রান্তি দেখা দিয়েছে কারণ এখানে ফরওয়ার্ডিং রেফারেন্স সম্পর্কিত সংশ্লেষিত ছাড়ের গাইডের জন্য একটি নির্দিষ্ট ব্যতিক্রম রয়েছে।

এটি সত্য যে কনস্ট্রাক্টর থেকে উত্পন্ন শ্রেণীর টেম্পলেট আর্গুমেন্ট ছাড়ের উদ্দেশ্যে এবং ব্যবহারকারী-সংজ্ঞায়িত ছাড়ের গাইড থেকে উত্পন্ন উত্সের জন্য প্রার্থী ফাংশনটি দেখতে একেবারে একই দেখতে পায়, যেমন:

template<typename T>
auto f(T&&) -> Foo<T>;

তবে কন্সট্রাক্টর থেকে উত্পন্ন একের জন্য এটি T&&একটি সাধারণ মূল্যমানের রেফারেন্স, যখন এটি ব্যবহারকারী-সংজ্ঞায়িত ক্ষেত্রে ফরওয়ার্ডিং রেফারেন্স । এটি সিটি ++ 17 স্ট্যান্ডার্ডের ( টেম্পেডটুক্টল কল] / 3 দ্বারা নির্দিষ্ট করা হয়েছে (খসড়া এন 4659, খনিতে জোর দিন):

একটি ফরোয়ার্ডিং রেফারেন্স হ'ল সিভি-অযোগ্য টেম্পলেট প্যারামিটারের মূল মূল্যবোধ যা কোনও শ্রেণীর টেমপ্লেটের একটি টেম্পলেট প্যারামিটারকে উপস্থাপন করে না (শ্রেণি টেম্পলেট আর্গুমেন্ট ছাড়ের সময় ([over.match.class.dedct]))।

সুতরাং ক্লাস কন্সট্রাক্টর থেকে সংশ্লেষিত প্রার্থী Tযদি কোনও ফরোয়ার্ডিং রেফারেন্স (যা Tলভালু রেফারেন্স হিসাবে অনুদান করতে পারে T&&, তবে এটি একটি লভালু রেফারেন্সও হ'ল) ​​কেটে নেবে না, তবে পরিবর্তে কেবল Tঅ-রেফারেন্স হিসাবেই কেটে যাবে , তাই T&&সর্বদা একটি মূল্যবোধ রেফারেন্স।


1
পরিষ্কার এবং সংক্ষিপ্ত উত্তরের জন্য আপনাকে ধন্যবাদ। আপনি কী জানেন যে কেন ফরওয়ার্ডিং রেফারেন্সের নিয়মে ক্লাস টেম্পলেট প্যারামিটারগুলির ব্যতিক্রম আছে?
jtbandes

2
@ জেটব্যান্ডেস মার্কিন জাতীয় সংস্থার একটি মন্তব্যের ফলে এটি পরিবর্তন করা হয়েছে বলে মনে হয়, পেজ p0512r0 দেখুন । যদিও আমি মন্তব্যটি খুঁজে পাইনি। যৌক্তিকতার জন্য আমার অনুমান যে আপনি যদি কোনও নির্মাণকর্তা কোনও মূল্যের রেফারেন্স গ্রহণ করে লিখেন তবে আপনি সাধারণত এটি একইভাবে কাজ করার প্রত্যাশা করেন যে আপনি কোনও Foo<int>(...)বা ন্যায়বিচার নির্দিষ্ট করেছেন Foo(...), যা ফরোয়ার্ডিং রেফারেন্সগুলির ক্ষেত্রে নয় (যা Foo<int&>পরিবর্তে ছাড়িয়ে দিতে পারে
আখরোট

6

এখানে ইস্যু যে যেহেতু হয়, বর্গ উপর টেমপ্লেট করা হয় Tকন্সট্রাকটর এ, Foo(T&&)আমরা না করণ টাইপ সিদ্ধান্তগ্রহণ; আমাদের সবসময়ই একটি আর-মান রেফারেন্স থাকে। এটি হ'ল, Fooনির্মাতা প্রকৃতপক্ষে এর মতো দেখায়:

Foo(int&&)

Foo(2)কাজ কারণ 2একটি মূল্য আছে।

Foo(x)না কারণ xএটি এমন একটি মূল্যমান যা আবদ্ধ হতে পারে না int&&। আপনি std::move(x)এটি উপযুক্ত ধরণের ( ডেমো ) কাস্ট করতে পারেন

Foo<int&>(x)ঠিকঠাক কাজ করে কারণ কনস্ট্রাক্টর Foo(int&)রেফারেন্স বিধ্বস্ত হওয়ার কারণে হয়ে যায় ; প্রাথমিকভাবে এটি স্ট্যান্ডার্ড অনুযায়ী Foo((int&)&&)পতিত হয় Foo(int&)

আপনার "অনর্থক" ছাড়ের গাইডের ক্ষেত্রে: প্রাথমিকভাবে কোডটির জন্য একটি ডিফল্ট টেম্পলেট ছাড়ের গাইড রয়েছে যা মূলত এর মতো সহায়ক হিসাবে কাজ করে:

template<typename T>
struct Foo {
    Foo(T&&) {}
};

template<typename T>
Foo<T> MakeFoo(std::add_rvalue_reference_t<T> value)
{
   return Foo<T>(std::move(value));
}

//... 
auto f = MakeFoo(x);

এর কারণ এটি স্ট্যান্ডার্ড নির্দেশ করে যে এই (কাল্পনিক) টেমপ্লেট পদ্ধতিতে বর্গ হিসাবে একই টেম্পলেট প্যারামিটার রয়েছে (জাস্ট T) নির্ধারক হিসাবে কোনও টেম্পলেট পরামিতি (এই ক্ষেত্রে কোনওটিই নয়; নির্মাণকারী টেম্পলেট নয়)। তারপরে, ফাংশন প্যারামিটারগুলির ধরণগুলি কনস্ট্রাক্টরের মতো। আমাদের ক্ষেত্রে, তাত্ক্ষণিকতার পরে Foo<int>, নির্মাণকারীর মতো মনে হচ্ছে Foo(int&&), অন্য কথায় একটি মূল্য-রেফারেন্স। সুতরাং add_rvalue_reference_tউপরের ব্যবহার ।

স্পষ্টতই এটি কাজ করে না।

আপনি যখন আপনার "রিডানডান্ট" ছাড়ের গাইড যোগ করবেন:

template<typename T>
Foo(T&&) -> Foo<T>;

আপনি সংযুক্ত রেফারেন্স কোন ধরনের সত্ত্বেও, কম্পাইলার যে পার্থক্য করার অনুমতি দেওয়া Tকন্সট্রাকটর (ইন int&, const int&অথবা int&&ইত্যাদি), আপনি টাইপ বর্গ রেফারেন্স (ঠিক ছাড়া হতে জন্য আমাদের অনুমিত অভিপ্রেত T)। কারণ হঠাৎ আমরা টাইপ অনুমান সম্পাদন করছি

এখন আমরা আর একটি (কাল্পনিক) সহায়ক ফাংশন উত্পন্ন করি যা এর মতো দেখায়:

template<class U>
Foo<U> MakeFoo(U&& u)
{
   return Foo<U>(std::forward<U>(u));
}

// ...
auto f = MakeFoo(x);

(নির্মাণকারীর কাছে আমাদের কলগুলি ক্লাস টেম্পলেট যুক্তি ছাড়ের উদ্দেশ্যে সহায়িকার ফাংশনে পুনঃনির্দেশিত করা হয়, তাই Foo(x)হয়ে যায় MakeFoo(x))।

এর ফলে U&&পরিণত int&এবং Tকেবল পরিণতint


টেম্প্লেটিংয়ের দ্বিতীয় স্তরেরটি প্রয়োজনীয় বলে মনে হয় না; এটি কোন মূল্য সরবরাহ করে? আপনি কিছু ডকুমেন্টেশন করার জন্য একটি লিঙ্ক আছে যা সুস্পষ্ট প্রদান করতে পারেন কেন টি && সবসময় একটি rvalue রেফারেন্স এখানে হিসাবে গণ্য হবে?
jtbandes

1
তবে যদি টি কে এখনও ছাড় না করা হয়, তবে "টি তে কোনও ধরণের রূপান্তরযোগ্য" এর অর্থ কী?
jtbandes

1
আপনি দ্রুত কাজের প্রস্তাব দিচ্ছেন, তবে আপনি কোনও উপায়ে এটি পরিবর্তন না করে কেন এটি কাজ করে না সে কারণে আপনি ব্যাখ্যাটিতে আরও মনোযোগ দিতে পারেন? কেন এটি যেমন কাজ করে না? " xএমন একটি মূল্যমান যা বেঁধে রাখতে পারে না int&&" তবে যে কেউ বুঝতে পারে না সে হতবাক হয়ে যাবে যা Foo<int&>(x)কাজ করতে পারে তবে স্বয়ংক্রিয়ভাবে সন্ধান করা হয়নি - আমি মনে করি আমরা সবাই কেন এটির আরও গভীর বোঝা চাই।
উইক

2
@ উইক: কেন আরও ফোকাস করতে পোস্টটি আপডেট করেছি।
অ্যান্ডি

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