نوع المؤشر لعضو من الفئة الأساسية
-
27-09-2019 - |
سؤال
لدي مشكلة فيما يتعلق بمؤشرات الأعضاء. فشل الرمز التالي في تجميع كل من 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>
معلمة قالب صريح إلى الكود الخاص بك.