c++ - فشل الإحلال باستخدام "std:: function" ومعلمة القالب المستخرجة سابقًا-لماذا؟




c++11 templates (2)

النظر في التعليمات البرمجية التالية:

template <typename> 
struct S { };

void g(S<int> t);

template <typename T>
void f(T, std::function<void(S<T>)>);

عند محاولة الاحتجاج

f(0, g);

أحصل على الخطأ التالية:

error: no matching function for call to 'f'
    f(0, g);
    ^

note: candidate template ignored: could not match 
      'function<void (S<type-parameter-0-0>)>' 
      against 'void (*)(S<int>)'
void f(T, std::function<void(S<T>)>);
     ^

مثال حي على godbolt.org

على الرغم من أنني أفهم أنه لا يمكن استنتاج نوع المعلمة std::function عمومًا لأنه سياق غير مستخلص

في هذه الحالة ، يمكن أولاً استنتاج T بواسطة الوسيطة التي تم تمريرها 0 ، ثم استبدالها في std::function<void(S<T>)> للحصول على std::function<void(S<int>)> .

أتوقع أنه بعد استنتاج T=int ، سيقوم المترجم باستبدال T في كل مكان في التوقيع ثم محاولة إنشاء المعلمة std::function مع الوسيطة g .

لماذا هذا ليس هو الحال؟ أفترض أن الترتيب الذي يحدث فيه الاستبدال / الاستنتاج له علاقة بهذا الأمر ، لكنني أود أن أرى الصيغة القياسية ذات الصلة.

سؤال مهم : هل هذا شيء يمكن تغييره في أي معيار قياسي في المستقبل مع الحفاظ على التوافق مع الإصدارات السابقة ، أم أن هناك سببًا أساسيًا لعدم نجاح هذا النوع من الاستبدال؟


على الرغم من أنني أفهم أنه لا يمكن استنتاج نوع المعلمة std::function عمومًا لأنه سياق غير مستخلص ، في هذه الحالة يمكن أولاً استنتاج T بواسطة الوسيطة التي تم تمريرها 0 .

هذا ليس صحيحا. T استنتاجي في هذا السياق. إذا قمت بتغيير الرمز إلى

template <typename T>
void f(std::function<void(S<T>)>);

int main()
{
    f(std::function<void(S<int>)>(g));
}

سوف ترجمة التعليمات البرمجية و T بشكل صحيح استنتاجها.

مشكلتك هي أنك تقوم بتمرير كائن إلى الوظيفة التي لا يمكن استخراج T منها. لن يقوم المحول البرمجي بأي تحويل للوسيطات الوظيفية عندما يحاول استنتاج T هذا يعني أن لديك int و دالة لأن الأنواع التي تم تمريرها إلى الدالة. يحصل على int من 0 ، ثم يحاول الحصول على النوع من الدالة std::function التي تمر بها في المعلمة الثانية ، لكن بما أنك لم تنتقل إلى std::function فلا يمكن استخراج T ، T السبب ، تحصل على خطأ.


على الرغم من أنني أفهم أنه لا يمكن استنتاج نوع المعلمة std :: function عمومًا لأنه سياق غير مستخلص

إنه ليس سياق غير مستخلص. بل على العكس تماما. لأنه يتم محاولة خصم المعلمة std::funcition ، ولكن الوسيطة ليست std::function ، يفشل الخصم. يجب أن يتفق استنباط وسيطات القوالب من وسيطات الدوال على كل وسيطات الدوال. إذا فشلت لأحد ، فإنه فشل تماما.

[temp.deduct.type]

2 في بعض الحالات ، يتم الخصم باستخدام مجموعة واحدة من الأنواع P و A ، وفي حالات أخرى ، سيكون هناك مجموعة من الأنواع المقابلة P و A. ويتم خصم النوع بشكل مستقل لكل زوج P / A ، ويتم خصم ثم يتم دمج قيم وسيطة القالب. إذا كان لا يمكن إجراء خصم الكتابة لأي زوج P / A ، أو إذا كان أي خصم يؤدي إلى أكثر من مجموعة محتملة من القيم المستخرجة ، أو إذا كانت أزواج مختلفة تعطي قيمًا مخصومة مختلفة ، أو إذا ظلت وسيطة القالب غير مستخرجة أو صريحة تم تحديد خصم وسيطة القالب المحدد.

إن جعل نوع معلمة الوظيفة الثانية في سياق غير مستخلص هو في الواقع كيف يمكن للمرء التغلب على الخطأ:

#include <functional>

template<typename T>
struct type_identity {
    using type = T;
};

template <typename> 
struct S { };

void g(S<int> ) {}

template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}

int main() {
    f(0, g);
}

يتم استنتاج T بنجاح من وسيطة الوظيفة الأولى ، ولا يوجد شيء لاستنباطه. لذلك يعتبر dedcution نجاحا.

Live





template-deduction