سؤال

لدي مشكلة فيما يتعلق بمؤشرات الأعضاء. فشل الرمز التالي في تجميع كل من Oracle Solaris Studio 12.2's CC و Cygwin GCC 4.3.4 ولكنه يعمل مع Microsoft Visual C ++ 2010:

struct A {
  int x;
};

struct B : public A {
};

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

int main(int, char *[]) {
    Bar<B> bbar;
    bbar.foo(&B::x);
    return 0;
}

في السطر التالي إلى الماضي ، فشل كلا المترجمين المذكورة أعلاه في العثور على تطابق ل Bar<B>::foo(int A::*). كتبت اختبارًا بسيطًا للتأكيد على أن نوع التعبير &B::x هو في الواقع int A::*:

// ...

static void foo(int A::*p) {
  std::cout << "A" << std::endl;
}

static void foo(int B::*p) {
  std::cout << "B" << std::endl;
}

int main(int, char *[]) {
    foo(&B::x);  // prints "A", even on MS VC++ 2010 
    return 0;
}

يعمل الحل البديل التالي مع GCC (لم يتم اختباره مع Oracle CC حتى الآن) ولكنه يفشل مع VC ++ بسبب الغموض:

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
  template<typename M, typename _T_base> inline void foo(M _T_base::*p) {
      foo(static_cast<M T::*>(p));
  }
};

سؤالي: ما هو السلوك الصحيح؟ يبدو أن VC ++ يقوم بعمل ضمني من int A::* إلى int B::* لتلبية المكالمة إلى قالب وظيفة العضو ، ألا ينبغي للمترجمين الآخرين أن يفكروا في فعل الشيء نفسه؟

هل كانت مفيدة؟

المحلول

تحويل من int A::* إلى int B::* مسموح به ، وهذه ليست المشكلة. المشكلة في خصم الوسيطة القالب ، حيث يمكنك معرفة ما إذا كنت تجرب البرنامج التالي الذي يوفر وسيطة قالب <int> إلى عن على B::foo وتجميع وظيفة غير عضو foo2 الذي ينتج نفس الخطأ مثل B::foo فعلت من قبل.

struct A {
  int x;
};

struct B : public A {
};

template <typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

template<typename M> void foo2(M B::*p);

int main(int, char*[]) {
  Bar<B> bbar;
  bbar.foo<int>(&B::x);
  foo2(&B::x); // error, but foo2<int>(&B::x) would work.
  return 0;
}

أعتقد أن هذا الموقف غير مشمول بالحالات التي من المفترض أن يستنتج فيها المترجم حجة القالب <int> من تلقاء نفسها. 14.8.2.1p3:

بشكل عام ، تحاول عملية الاستنتاج العثور على قيم وسيطة القالب التي ستجعل المستخلص مطابقة لـ A (بعد تحويل النوع A كما هو موضح أعلاه). ومع ذلك ، هناك ثلاث حالات تسمح فرقًا:

  • إذا كان P الأصلي نوعًا مرجعيًا ، يمكن أن يكون النوع المستخلص (أي ، النوع المشار إليه بواسطة المرجع) أكثر تأهيلا من CV من A.
  • يمكن أن يكون A مؤشرًا أو مؤشرًا آخر إلى نوع العضو الذي يمكن تحويله إلى A Destected A عبر تحويل مؤهل (Conv.Qual).
  • إذا كانت p عبارة عن فئة ، وكان P يحتوي على معرف قالب النموذج ، فيمكن أن يكون A فئة مشتقة من A. المستنتج بالمثل ، إذا كان P مؤشرًا لفئة من معرف قالب النموذج ، يمكن أن يكون مؤشرًا فئة مشتقة أشار إليها أ.

هنا "P" هو نوع وسيطة وظيفة القالب: M B::*p, ، حيث معلمة نوع القالب M هو أن يتم تحديدها. "A" هو نوع الوسيطة الفعلية: int A::*. P و A بالتأكيد ليسوا مرجعين أو فئة ، ونوع التحويل من المؤشر إلى الأعضاء ، سنحتاج إلى عمل هذا ليس تحولًا مؤهلًا (الذي يصف فقط التلاعب/المتقلبة مثل X* إلى const X* أو int X::* إلى const int X::*).

لذلك لا يمكن استنتاج وسيطة القالب ، ويجب عليك إضافة <int> معلمة قالب صريح إلى الكود الخاص بك.

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