سؤال

أثناء قراءة سؤال آخر، واجهت مشكلة في الطلب الجزئي، والتي قمت بقصها إلى حالة الاختبار التالية

template<typename T>
struct Const { typedef void type; };

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

int main() {
  // GCC chokes on f(0, 0) (not being able to match against T1)
  void *p = 0;
  f(0, p);
}

بالنسبة لكلا قالبي الوظائف، يكون نوع وظيفة التخصص الذي يدخل في تحليل التحميل الزائد هو void(int, void*).لكن الترتيب الجزئي (وفقًا لـ Comeau وGC) يشير الآن إلى أن القالب الثاني أكثر تخصصًا.لكن لماذا؟

اسمحوا لي أن أتناول الطلب الجزئي وأبين أين لدي أسئلة.يمكن Q يكون نوعًا مكياجًا فريدًا يستخدم لتحديد الترتيب الجزئي وفقًا لـ 14.5.5.2.

  • قائمة المعلمات المحولة لـ T1 (س مدرجة): (Q, typename Const<Q>::type*).أنواع الحجج هي AT = (Q, void*)
  • قائمة المعلمات المحولة لـ T2 (س مدرجة): BT = (Q, void*), ، وهي أيضًا أنواع الوسائط.
  • قائمة المعلمات غير المحولة لـ T1: (T, typename Const<T>::type*)
  • قائمة المعلمات غير المحولة لـ T2: (T, void*)

نظرًا لأن C++ 03 لا يحدد ذلك بشكل كافٍ، فقد استخدمت النية التي قرأت عنها في العديد من تقارير العيوب.قائمة المعلمات المحولة أعلاه لـ T1 (مُسَمًّى AT بواسطتي) يتم استخدامه كقائمة وسيطة لـ 14.8.2.1 "استنتاج وسيطات القالب من استدعاء دالة".

14.8.2.1 لا يحتاج إلى تحويل AT أو BT لم يعد نفسه (مثل إزالة المُصرِحين المرجعيين، وما إلى ذلك)، وينتقل مباشرة إلى 14.8.2.4, ، والتي بشكل مستقل لكل منهما A / P يقوم الزوج بكتابة الخصم:

  • AT ضد T2: { (Q, T), (void*, void*) }. T هي معلمة القالب الوحيدة هنا، وسوف تجد ذلك T لا بد وأن Q.نوع الخصم ينجح بشكل تافه ل AT ضد T2.

  • BT ضد T1: { (Q, T), (void*, typename Const<T>::type*) }.وسوف تجد ذلك T يكون Q, ، هنا أيضًا. typename Const<T>::type* هو سياق غير مستنتج، وبالتالي لن يتم استخدامه لاستنتاج أي شيء.


وهنا سؤالي الأول:هل سيستخدم هذا الآن قيمة T استنتاج للمعلمة الأولى؟إذا كانت الإجابة لا، فإن القالب الأول هو الأكثر تخصصا.لا يمكن أن يكون هذا هو الحال، لأن كلا من مجلس التعاون الخليجي وComeau يقولان أن القالب الثاني أكثر تخصصًا، ولا أعتقد أنهما مخطئان.لذلك نفترض "نعم"، وندرج void* داخل T.الفقرة (14.8.2.4) يقول "يتم الخصم بشكل مستقل لكل زوج ثم يتم دمج النتائج" و أيضا "ومع ذلك، في سياقات معينة، لا تشارك القيمة في خصم النوع، ولكنها تستخدم بدلاً من ذلك قيم وسيطات القالب التي تم استنتاجها في مكان آخر أو تم تحديدها بشكل صريح." هذا يبدو وكأنه "نعم" أيضًا.

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

لذلك سؤالي الثاني:الآن، لماذا تقول التطبيقات أن القالب الثاني أكثر تخصصًا؟ما هي النقطة التي أغفلتها؟


يحرر:لقد اختبرت التخصص الصريح وإنشاء مثيل، وكلاهما، في الإصدارات الأخيرة لدول مجلس التعاون الخليجي (4.4) أخبرني أن الإشارة إلى التخصص غامضة، في حين أن الإصدار الأقدم من دول مجلس التعاون الخليجي (4.1) لا يرتفع ذلك الغموض خطأ.يشير هذا إلى أن إصدارات دول مجلس التعاون الخليجي الأخيرة بها ترتيب جزئي غير متناسق لقوالب الوظائف.

template<typename T>
struct Const { typedef void type; };

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

template<> void f(int, void*) { }
  // main.cpp:11: error: ambiguous template specialization 
  // 'f<>' for 'void f(int, void*)'
هل كانت مفيدة؟

المحلول

ها هي الذهاب في هذا. أنا أتفق مع تشارلز بيلي أن الخطوة غير الصحيحة هي الذهاب من Const<Q>::Type* ل void*

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

الخطوات التي نريد اتخاذها هي:

14.5.5.2/2

نظرا لقوالب الوظائف المثالية، ما إذا كان أحد أكثر تخصيصا من الآخر يمكن تحديده عن طريق تحويل كل قالب بدوره واستخدام خصم الوسائد (14.8.2) لمقارنته بالآخر.

14.5.5.5.2/3-b1.

لكل معلمة قالب من النوع، توليف نوع فريد من نوعه واستبدال أنه لكل حدوث هذه المعلمة في قائمة معلمات الوظيفة، أو للحصول على وظيفة تحويل القالب، في نوع الإرجاع.

في رأيي، يتم تصنيع الأنواع على النحو التالي:

(Q, Const<Q>::Type*)    // Q1
(Q, void*)              // Q2

لا أرى أي صياغة تتطلب أن تكون المعلمة الثانية للتوليف T1 يكون void*. وبعد لا أعرف أي سابقة لذلك في سياقات أخرى أيضا. نوع Const<Q>::Type* هو نوع صالح تماما داخل نظام C ++ نوع.

حتى الآن نحن نقوم بخطوات الخصم:

Q2 إلى T1.

نحاول استنتاج معلمات القالب ل T1 لذلك لدينا:

  • المعلمة 1: T يتم استنتاج ليكون Q
  • المعلمة 2: سياق غير مقصود

على الرغم من أن المعلمة 2 هي سياق غير متخصص، إلا أن الخصم قد نجح لأن لدينا قيمة ل T.

q1 إلى t2.

خصم معلمات القالب ل T2 لدينا:

  • المعلمة 1: T يتم استنتاج ليكون Q
  • المعلمة 2: void* لا يطابق Const<Q>::Type* فشل خصم ذلك.

IMHO، إليك المكان الذي يتيح لنا المعيار. ومع ذلك، فإن المعلمة لا تعتمد لذلك من غير الواضح حقا ما يجب أن يحدث، ومع ذلك، فإن تجربتي (بناء على قراءة تحفيرة تبلغ 14.8.2.1/3) هي أنه حتى حيث لا يعتمد نوع المعلمة P، ثم يجب أن تطابق نوع الوسيطة A هو - هي.

يمكن استخدام الوسائط المركبة من T1 لتخصص T2، ولكن ليس العكس. T2 أكثر تخصصا من T1 وهكذا هي أفضل وظيفة.


التحديث 1:

فقط لتغطية poing حول Const<Q>::type كونها باطلة. النظر في المثال التالي:

template<typename T>
struct Const;

template<typename T>
void f(T, typename Const<T>::type*) // T1
{ typedef typename T::TYPE1 TYPE; }

template<typename T>
void f(T, void*)                    // T2
{ typedef typename T::TYPE2 TYPE ; }

template<>
struct Const <int>
{
  typedef void type;
};

template<>
struct Const <long>
{
  typedef long type;
};

void bar ()
{
  void * p = 0;
  f (0, p);
}

في ما سبق، Const<int>::type يستخدم عند إجراء قواعد حل الزائد المعتادة، ولكن ليس عندما نصل إلى قواعد التحميل الزائد الجزئي. لن يكون صحيحا لاختيار تخصص تعسفي ل Const<Q>::type. وبعد قد لا تكون بديهية، لكن المحول البرمجي سعداء للغاية بحيث يكون لديه نوع معين من النموذج Const<Q>::type* واستخدامها أثناء خصم النوع.


تحديث 2.

template <typename T, int I>
class Const
{
public:
  typedef typename Const<T, I-1>::type type;
};

template <typename T>
class Const <T, 0>
{
public:
  typedef void type;
};

template<typename T, int I>
void f(T (&)[I], typename Const<T, I>::type*)     // T1
{ typedef typename T::TYPE1 TYPE; }

template<typename T, int I>
void f(T (&)[I], void*)                           // T2
{ typedef typename T::TYPE2 TYPE ; }


void bar ()
{
  int array[10];
  void * p = 0;
  f (array, p);
}

عندما Const يتم إنشاء قالب مع بعض القيمة I, ، ينفسه بشكل متكرر نفسه حتى I يصل إلى 0. هذا هو عندما التخصص الجزئي Const<T,0> تم الإختيار. إذا كان لدينا مترجم يقوم بتوالي بعض النوع الحقيقي لمعلمات الوظيفة، فما القيمة التي ستتخخر التحويل البرمجي لمؤشر الصفيف؟ قل 10؟ حسنا، سيكون هذا جيدا للمثال أعلاه ولكنه لن يطابق التخصص الجزئي Const<T, 10 + 1> والتي، من الناحية النظرية على الأقل، ستؤدي إلى عدد لا حصر له من إنشاءات العودية الأساسية. مهما كانت القيمة التي حددها يمكننا تعديل شرط النهاية لتكون القيمة + 1، ثم لدينا حلقة لا حصر لها في خوارزمية الطلب الجزئي.

أنا لا أرى كيف يمكن أن تقوم خوارزمية الطلب الجزئي بإنشاء مثيل Const للعثور على ما type حقا هو.

نصائح أخرى

تحرير: بعد الدراسة كلانغ التنفيذ (بواسطة دوغ جريجور) من خوارزمية الطلب الجزئي الخاصة بهم، أتفق مع بقية الملصقات أن المثال الأصلي لا "المقصود" غامض - على الرغم من أن المعيار ليس واضحا كما يمكن أن يكون حول ما يجب أن يحدث في مثل هذه الحالات. لقد قمت بتحرير هذا المنشور للإشارة إلى أفكاري المنقحة (للحصول على استحقاقاتي المرجعية الخاصة بي). ولا سيما خوارزمية Clang أوضح ذلكtypename Const<T>::type"لا يترجم إلى" الفراغ "خلال خطوة الطلب الجزئي - وأن كل زوج A / P يستمر مستقلا عن بعضها البعض.

في البداية تساءلت لماذا اعتبر ما يلي غامضة:

        template<class T> void f(T,T*);  // 1

        template<class T> void f(T, int*); // 2

        f(0, (int*)0); // ambiguous

(The above is ambiguous because one cannot deduce f1(U1,U1*) from f2(T,int*), and going the other way, one cannot deduce f2(U2,int*) from f1(T,T*). Neither is more specialized.)

لكن ما يلي لن يكون غامضا:

        template<class T> struct X { typedef int type; };
        template<class T> void f(T, typename X<T>::type*); // 3
        template<class T> void f(T, int*); // 2

(السبب يمكن أن يتوقع المرء أن يكون غامضا إذا كان ما يلي سيحدث:
- f3(U1,X<U1>::type*) -> f3(U1, int*) ==> f2(T,int*) (deduction ok, T=U1)
- f2(U2,int*) ==> f3(T, X<T>::type*) (deduction ok, T=U2 makes X<U2>::type* -> int*)
إذا كان هذا صحيحا، فلن يكون أحد أكثر تخصصا من الآخر.)

بعد دراسة خوارزمية ترتيب CLANG الجزئية، من الواضح أنهم يعاملون "3" أعلاه كما لو كان:

template<class T, class S> void f(T, S*); // 4

حتى خصم بعض "U" فريد من نوعه ضد "TypeName X :: Type" ستنجح -

  • f3(U1,X<U1>::type*) is treated as f3(U1, U2*) ==> f2(T,int*) (deduction not ok)
  • f2(U2,int*) ==> f3(T,S* [[X<T>::type*]]) (deduction ok, T=U2, S=int)

وهكذا "2" هو أكثر تخصيصا من الواضح من "3".

قائمة المعلمات المحولة ل T1 (q المدرجة): (q، typeename const :: النوع *). أنواع الوسائط موجودة = (Q، باطلة *)

أتساءل عما إذا كان هذا هو تبسيط صحيح صحيح. عند توليف النوع Q, ، هل المسموح بالاستحواض من التخصص Const لأغراض تحديد ترتيب قوالب Specliazation؟

template <>
struct Const<Q> { typedef int type; }

هذا يعني ذلك T2 ليس على الأقل متخصصة T1 لأن أ void* المعلمة لا تتطابق T1المعلمة الثانية لأي معلمات قالب معين.

يحرر:يرجى تجاهل هذا المنشور - بعد دراسة خوارزمية clans للترتيب الجزئي كما تم تنفيذها بواسطة Doug Gregor (على الرغم من أنه تم تنفيذها جزئيًا فقط حتى كتابة هذه السطور - يبدو أن المنطق ذي الصلة بسؤال OP تم تنفيذه بشكل كافٍ) - يبدو كما يلي: إذا كان يعامل السياق غير المستنتج على أنه مجرد معلمة قالب أخرى.مما يشير إلى أن التحميل الزائد مع وسيطة void* الصريحة يجب أن يكون الإصدار الأكثر تخصصًا ويجب ألا يكون هناك أي غموض.كالعادة كومو على حق.الآن بالنسبة للصياغة في المعيار التي تحدد هذا السلوك بوضوح - فهذه مسألة أخرى ...

نظرًا لأن هذا المنشور تم نشره أيضًا على comp.lang.c++.moderated، ويبدو أنه يسبب بعض الارتباك هناك أيضًا - اعتقدت أنني سأنشر إجابتي لهذه المجموعة هنا أيضًا - حيث من الواضح أن المناقشة ذات صلة بالسؤال المطروح هنا .

On Jul 25, 1:11 pm, Bart van Ingen Schenau <b...@ingen.ddns.info> wrote:

You are going one step too fast here. How do you know (and would the compiler know) that there is no specialisation of Const<Q> such that Const<Q>::type != void?

As far as I can see, the compiler would transform the parameter-list of A to: AT=(Q, <unknown>*). To call B with these parameters requires an implicit conversion (<unknown>* to void*) and therefore A is less specialised than B.

وأعتقد أن هذا غير صحيح.عند التحقق لمعرفة الوظيفة الأكثر متخصص (أثناء الترتيب الجزئي) ، يقوم المترجم بتحويل قائمة المعلمات إلى (Q, void*) - أي.إنه في الواقع ينشئ مثيلا ذا صلة قالب (أفضل مطابقة) ويبحث بداخله عن قيمة "النوع" - في هذه الحالة ، استنادا إلى في القالب الأساسي ، سيكون باطلا *.

فيما يتعلق بوجهة نظرك فيما يتعلق بالتخصص الجزئي - عند التحقق من أي قالب أكثر تخصصا من الآخر ، النوع الوحيد الذي يمكن استخدامه هو النوع الفريد الذي تم إنشاؤه - إذا كانت هناك تخصصات أخرى في هذه النقطة إنشاء مثيل للإعلان (عند الانتهاء من حل الحمل الزائد) سيتم النظر فيها.إذا قمت بإضافتها لاحقا ، ويجب تحديدها سوف تنتهك ODR (وفقا ل 14.7.4.1)

سيتم أيضا النظر في التخصصات الجزئية / الصريحة خلال تشكيل مجموعة المرشحين - ولكن هذه المرة باستخدام أنواع الحجج الفعلية إلى الوظيفة.إذا كان أفضل تخصص جزئي مطابق (ل X) ينتج عنه نوع الوظيفة الذي يحتوي على تسلسل تحويل ضمني أفضل للبعض المعلمة ، ثم لا نصل أبدا إلى مرحلة الترتيب الجزئي ، وهذا سيتم اختيار وظيفة "أفضل" (قبل الوصول إلى الجزء الجزئي مرحلة الطلب)

فيما يلي مثال مع التعليقات حول ما يجب أن يحدث في الخطوات المختلفة:

    template<class T, bool=true> struct X;  // Primary

    template<class T> struct X<T,true> { typedef T type; };  // A
    template<> struct X<int*,true> { typedef void* type; };  // B


    template<class T> void f(T,typename X<T>::type); //1
    template<class T> void f(T*,void*); //2


    int main()
    {
      void* pv;
      int* pi;


      f(pi,pi);   
      // two candidate functions: f1<int*>(int*,void*),  f2<int>(int*,void*)
      // Note: specialization 'B' used to arrive at void* in f1
      // neither has a better ICS than the other, so lets partially order
      // transformed f1 is f1<U1>(U1,X<U1,true>::type) --> f1<U1>(U1,U1) 
      //       (template 'A' used to get the second U1)
      // obviously deduction will fail (U1,U1) -> (T*,void*)
      // and also fails the other way (U2*, void*) -> (T,X<T>::type)
      // can not partially order them - so ambiguity 




      f(pv,pv);  
      // two candidate functions: f1<void*>(void*,void*), f2<void>(void*,void*)
      // Note: specialization 'A' used to arrive at second void* in f1
      // neither has a better ICS than the other, so lets partially order
      // transformed f1 is f1<U1>(U1,X<U1>::type) --> f1<U1>(U1,U1) 
      //       (template 'A' used to get the second U1)
      // obviously deduction will fail (U1,U1) -> (T*,void*)
      // and also fails the other way (U2*, void*) -> (T,X<T>::type)
      // can not partially order them - so ambiguity again             

    }

تجدر الإشارة أيضا إلى أنه إذا لم يكن للقالب الأساسي تعريف - فإن SFINAE يعمل أثناء مرحلة الطلب الجزئي ، لا يمكن استنتاج أي منهما من الآخر ، ويجب أن ينتج الغموض.

وأيضًا إذا أضفت قالبًا آخر من شأنه أن يؤدي إلى مطابقة أخرى إذا تم نقل نقطة إنشاء أي من هذه الوظائف إلى مكان آخر في وحدة الترجمة، فسوف تنتهك بوضوح ODR.

On Jul 25, 1:11 pm, Bart van Ingen Schenau <b...@ingen.ddns.info> wrote:

أولاً، أن تكون أكثر تخصصاً يعني أن هذه هي أقل أنواع حيث يمكن تحديد هذا القالب عن طريق دقة التحميل الزائد.وباستخدام هذا، يمكن تلخيص قواعد الترتيب الجزئي على النحو التالي:حاول أن ابحث عن نوع ل A بحيث يمكن استدعاء A ولكن B لا ، أو الحمل الزائد القرار يفضل استدعاء A.إذا كان من الممكن العثور على هذا النوع ، فإن B هو أكثر متخصصة من أ.

لا حجة هنا.ولكن استنادا إلى القواعد كما هي حاليا ، يجب أن يكون مثال البروتوكول الاختياري غامضه.


أخيرًا، فيما يلي إجابات صريحة لا لبس فيها على السؤالين المحددين اللذين أثارهما litb:

1) هل سيستخدم هذا الآن قيمة T المستنتجة للمعلمة الأولى؟
نعم - بالطبع ، يجب أن تقوم بخصم حجة القالب - يجب الحفاظ على "الروابط".

2) الآن، لماذا تقول التطبيقات أن الثاني أكثر تخصصًا بدلاً من ذلك؟
لأنهم مخطئون ;)

آمل أن يؤدي هذا إلى حل المشكلة - يرجى إعلامي إذا كان هناك أي شيء لا يزال غير واضح :)

يحرر:أثار Litap نقطة جيدة في تعليقه - ربما يذكر أن القالب الأساسي سيحصل دائما تستخدم لإنشاء مثيل مع النوع الفريد الذي تم إنشاؤه هو بيان قوي للغاية.
هناك حالات لن يتم فيها استدعاء القالب الأساسي.
ما أحصل عليه هو أنه عند حدوث طلب جزئي ، يكون هناك نوع فريد تم إنشاؤه تستخدم لتتناسب مع أفضل تخصص.أنت على حق، ليس من الضروري أن يكون القالب الأساسي.لقد قمت بتحرير اللغة أعلاه للقيام بذلك.كما أثار أيضًا مشكلة تتعلق بتحديد قالب مطابقة أفضل بعد نقطة إنشاء مثيل.سيكون ذلك بمثابة انتهاك لـ ODR وفقًا للقسم الخاص بنقطة إنشاء مثيل.


ينص المعيار على أنه بمجرد إنشاء أزواج الحسابات مستحقة الدفع (باستخدام قواعد التحويل كما هو موضح في temp.func.order) يتم استنتاجها مقابل بعضها البعض باستخدام خصم وسيطة القالب (temp.deduct) - ويعالج هذا القسم حالة السياقات غير المستنتجة، وإنشاء مثيل للقالب ونوعه المتداخل، وإثارة نقاط إنشاء مثيل.يعالج قسم temp.point انتهاكات ODR (يجب ألا يتغير معنى الترتيب الجزئي بغض النظر عن نقاط إنشاء مثيل داخل وحدة الترجمة).لا زلت لا أعلم من أين يأتي هذا الارتباك؟– فيصل فالي منذ ساعة واحدة [احذف هذا التعليق]

ليتب:"لاحظ أن الخطوة التي تضع Q في Const::type لإنشاء الوسائط لا تتم تغطيتها بشكل صريح بواسطة قاعدة SFINAE.تعمل قواعد SFINAE مع خصم الوسيطة، وتضع الفقرات التي تضع Q في قائمة معلمات وظيفة قالب الوظيفة في 14.5.5.2.'

يجب استخدام قواعد SFINAE هنا - كيف لا يمكن استخدامها؟أشعر أنه ضمني بما فيه الكفاية - لن أنكر أنه يمكن أن يكون أكثر وضوحا ، وبينما أشجع اللجنة على التوضيح هذا - لا أعتقد أنه يحتاج إلى توضيح لتفسير مثالك بشكل كاف.

واسمحوا لي أن أقدم طريقة واحدة للربط بينهما.من (14.8.2):"عند تحديد قائمة وسيطات قالب صريحة، يجب أن تكون وسيطات القالب متوافقة مع قائمة معلمات القالب ويجب أن ينتج عنها نوع دالة صالح كما هو موضح أدناه ؛خلاف ذلك اكتب الخصم فشل"

من (14.5.5.2/3) "التحول المستخدم هو:- لكل معلمة قالب نوع ، قم بتجميع نوع فريد واستبدله لكل ظهور هذه المعلمة في قائمة معلمات الدالة، أو لدالة تحويل القالب، في نوع الإرجاع."

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

من (14.5.5.2/4) "باستخدام قائمة معلمات الدالة المحولة ، قم بإجراء خصم الوسيطة مقابل قالب الوظيفة الأخرى.القالب المحول على الأقل متخصص مثل الآخر إذا وفقط إذا, ، ينجح الاستنتاج وأنواع المعلمات المستخلصة هي مطابقة تامة (لذلك لا يعتمد الخصم على التحويلات الضمنية).

إذا أدت قائمة معلمات الدالة المحولة إلى فشل الاستبدال، فإننا نعلم أن الاستنتاج لا يمكن أن ينجح.وبما أن الخصم لم ينجح ، فهو ليس متخصصا مثل الآخر - وهذا كل ما نحتاج إلى معرفته للمضي قدما في ترتيب جزئي للاثنين.

ليتب:أنا أيضًا لست متأكدًا مما يحدث في هذه الحالة: template<typename T> struct A; template<typename T> void f(T, typename A<T>::type); template<typename T> void f(T*, typename A<T>::type); التاكيد هذا رمز مكتوب ليكون رمزا صالحا ، ولكن عند القيام ب A::type ، سيفشل لأنه في ملف سياق تعريف القالب ، لم يتم تعريف A بعد " لاحظ أيضا أنه لا يوجد نقطة اهتمام محددة لمثيلات القالب الناتجة عن هذا نوع من الاستبدال أثناء محاولة تحديد الطلب (لا يعتمد الطلب الجزئي على أي سياق.إنها خاصية ثابتة لقالبين وظيفيين متضمنين).أعتقد أن هذا يبدو وكأنه مشكلة في المعيار والتي تحتاج إلى إصلاح.

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

الآن ، المعيار واضح أن الترتيب الجزئي لا أدري للنوع المستخدم في استدعاء الوظيفة (أعتقد هذا ما تشير إليه عندما تصفه بأنه خاصية ثابتة وكونه مستقلا عن السياق).

المعيار واضح أيضا أنه يهتم فقط بالترتيب الجزئي (يستدعي الترتيب الجزئي) بين قوالب الوظائف أثناء عملية حل الحمل الزائد (13.3.3/1) إذا وفقط إذا لم يتمكن من اختيار الوظيفة الأفضل بناء على ICS أو إذا كان أحدهما قالبا والآخر ليس كذلك.[الترتيب الجزئي للتخصصات الجزئية لقالب الفصل هو قضية منفصلة وفي رأيي يستخدم السياق ذي الصلة (تعريفات القوالب الأخرى) التي تتطلب إنشاء مثيل لتلك الفئة المعينة.]

وبالتالي ، في رأيي ، نظرا لأن آلية الترتيب الجزئي لقوالب الوظائف يتم استدعاؤها عند التحميل الزائد يتم تنفيذ القرار ، ويجب أن يستخدم جزءا ذا صلة من السياق (تعريفات القوالب والتخصصات) المتاحة عند النقطة التي يتم فيها حل الحمل الزائد.

لذا، بناءً على تفسيري، وفقًا لمثالك باستخدام "بنية القالب A" أعلاه، فإن الكود صالح.لا يتم الترتيب الجزئي في سياق التعريف.ولكن إذا / عندما يحدث لاستدعاء حل الحمل الزائد بين الوظيفتين عن طريق كتابة استدعاء إلى f ((int *) 0,0) - وفي ذلك الوقت عندما يكون المترجم إما يحاول تجميع إعلان مرشح أو طلبه جزئيا (إذا وصل إلى خطوة الترتيب الجزئي) إذا نتج عن تعبير أو نوع غير صالح كجزء من نوع الوظيفة ، فإن SFINAE يساعدنا ويخبرنا لنا أن يفشل خصم القالب (بقدر ما يتعلق الأمر بالطلب الجزئي ، فهذا يعني أن المرء لا يمكن أن يكون أكثر تخصصا من الآخر إذا لم نتمكن حتى من تحويل القالب).

الآن فيما يتعلق بالنقاط المهمة - إذا كنت مقتنعا ، كما أنا ، بأن أنواع الوظائف المحولة من المفترض أن تمثيل المثيلات الضمنية باستخدام قوائم وسيطات القالب المتوفرة بشكل صريح (باستخدام الأنواع التي تم إنشاؤها بشكل فريد) ثم الاقتباسات القياسية التالية ذات صلة:

14-6-4-1/1 بالنسبة لتخصص قالب دالة أو تخصص قالب دالة عضو أو تخصص في قالب دالة دالة عضو أو عضو بيانات ثابتة في قالب فئة، إذا تم إنشاء مثيل للتخصص ضمنيا لأنه مشار إليه من داخل تخصص قالب آخر والسياق الذي يشار إليه منه يعتمد على معلمة قالب ، نقطة إنشاء مثيل للتخصص هي نقطة إنشاء مثيل من التخصص المرفق.

الطريقة التي أفسر بها هذا هي أن نقطة الاهتمام لنوع الوظيفة المحولة ونوع الدالة الأصلية هي نفس نقطة الاهتمام لتلك الوظائف التي تم إنشاؤها بواسطة استدعاء الدالة الفعلية.

ليتب:منذ الطلب الجزئي هو بالأحرى فقط a property of the syntactic form of parameters (i.e "T*" against "T(*)[N]"), سأصوت لتعديل المواصفات (مثل "إذا ظهر Q في محدد اسم متداخل ل معرف مؤهل يسمي نوعا، ثم النوع المسمى هو "Q") أو القول بأن النوع المسمى هو نوع فريد آخر. This means that in template<typename T> void f(T, typename Const<T>::type*); the argument list is (Q, R*), for example. Same for template<typename T> void f(T*, typename ConstI<sizeof(T)>::type); the arg lisst would be (Q*, R). A similar rule would be needed for non-type parameters, of course.يجب أن أفكر في الأمر وأجري بعض حالات الاختبار لمعرفة ما إذا كان هذا سيؤدي إلى ترتيبات طبيعية.

آآه - أنت الآن تقترح حلا ممكنا يحل الغموض لصالح ما نحن كل توقع حدسي - هذه مشكلة منفصلة ، وبينما أحب الاتجاه الذي تتجه إليه ، مثلك ، أنا أيضا يجب أن أفكر فيه قبل الإعلان عن قابليته للتطبيق.

شكرا لمواصلة المناقشة.أتمنى ألا يقتصر الأمر على وضع التعليقات فقط.

نظرًا لأنه يمكنك تعديل مشاركاتي، فلا تتردد في الرد داخل المنشور إذا كان ذلك أسهل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top