كيفية اكتشاف ما إذا كان هناك متغير عضو معين في الفصل؟

StackOverflow https://stackoverflow.com/questions/1005476

سؤال

لإنشاء وظيفة قالب الخوارزمية، أحتاج إلى معرفة ما إذا كان x أو X (و y أو Y) في الفئة عبارة عن وسيطة قالب.قد يكون ذلك مفيدًا عند استخدام وظيفتي لفئة MFC CPoint أو فئة GDI+ PointF أو غيرها.كل منهم يستخدم علامة x مختلفة فيها.يمكن تقليل الحل الخاص بي إلى الكود التالي:


template<int> struct TT {typedef int type;};
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; }

struct P1 {int x; };
struct P2 {float X; };
// it also could be struct P3 {unknown_type X; };

int main()
{
    P1 p1 = {1};
    P2 p2 = {1};

    Check_x(p1); // must return true
    Check_x(p2); // must return false

    return 0;
}

لكنه لا يتم تجميعه في Visual Studio، أثناء التجميع في GNU C++.مع Visual Studio يمكنني استخدام القالب التالي:


template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }

لكنه لا يتم تجميعه في GNU C++.هل هناك حل عالمي؟

محدث:الهياكل P1 وP2 هنا هي على سبيل المثال فقط.يمكن أن يكون هناك أي فئات مع أعضاء غير معروفين.

ملاحظة.من فضلك، لا تنشر حلول C++ 11 هنا لأنها واضحة وغير ذات صلة بالسؤال.

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

المحلول

وهناك طريقة أخرى هي هذه واحدة، والتي تعتمد على <لأ href = "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html" يختلط = "noreferrer" > SFINAE للتعبير جدا. إذا كانت نتائج البحث عن اسم في الغموض، فإن المترجم رفض القالب

template<typename T> struct HasX { 
    struct Fallback { int x; }; // introduce member name "x"
    struct Derived : T, Fallback { };

    template<typename C, C> struct ChT; 

    template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
    template<typename C> static char (&f(...))[2]; 

    static bool const value = sizeof(f<Derived>(0)) == 2;
}; 

struct A { int x; };
struct B { int X; };

int main() { 
    std::cout << HasX<A>::value << std::endl; // 1
    std::cout << HasX<B>::value << std::endl; // 0
}

وأنها تقوم على فكرة رائعة من شخص ما في من الأعضاء.

ملحوظة: الشيكات HasX لأي عضو بيانات أو وظيفة تسمى X، مع نوع التعسفي. والغرض الوحيد من إدخال اسم عضو هو أن يكون هناك غموض محتمل لأعضاء اسم البحث - نوع من عضو غير مهم.

نصائح أخرى

وهنا هو الحل أبسط من يوهانس شواب - litb الصورة حساب . فهو يتطلب C ++ 11.

#include <type_traits>

template <typename T, typename = int>
struct HasX : std::false_type { };

template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

على تحديث : في. وهناك مثال سريع وشرح عن كيفية عمل ذلك

لهذه الأنواع:

struct A { int x; };
struct B { int y; };

ولدينا HasX<A>::value == true وHasX<B>::value == false. دعونا نرى ماذا.

ويذكر أن الأول std::false_type وstd::true_type يكون عضوا static constexpr bool اسمه value التي من المقرر أن false وtrue، على التوالي. وبالتالي، فإن قوالب اثنين HasX فوق يرث هذا العضو. (النموذج الأول من std::false_type والثانية من std::true_type).

ودعونا نبدأ بسيط ومن ثم المضي قدما خطوة خطوة حتى نصل إلى رمز أعلاه.

1) نقطة البداية:

template <typename T, typename U>
struct HasX : std::false_type { };

في هذه الحالة، ليس هناك مفاجأة: HasX مستمد من std::false_type وبالتالي HasX<bool, double>::value == false وHasX<bool, int>::value == false

2) التخلف U:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

وبالنظر إلى أن التخلف U إلى int، Has<bool> يعني في الواقع HasX<bool, int> وبالتالي، HasX<bool>::value == HasX<bool, int>::value == false.

و3) إضافة التخصص:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX<T, int> : std::true_type { };

في عام، وذلك بفضل القالب الأساسي، HasX<T, U> مستمد من std::false_type. ومع ذلك، هناك تخصص لU = int التي تستمد من std::true_type. ولذلك، HasX<bool, double>::value == false لكن HasX<bool, int>::value == true.

وبفضل الافتراضي لU، HasX<bool>::value == HasX<bool, int>::value == true.

و4) decltype وطريقة يتوهم أن أقول int:

واستطرادا قليلا هنا ولكن، من فضلك، تحمل معي.

وأساسا (وهذا ليس صحيحا تماما)، decltype(expression) ينتج نوع <م> التعبير . على سبيل المثال، 0 ديه اكتب int بالتالي، decltype(0) يعني int. بالقياس، 1.2 ديه نوع double وبالتالي، decltype(1.2) يعني double.

والنظر في وظيفة مع هذا الإعلان:

char func(foo, int);

وحيث foo هو نوع الطبقة. إذا f هو كائن من نوع foo، ثم decltype(func(f, 0)) يعني char (نوع إرجاعها بواسطة func(f, 0)).

والآن، يستخدم (1.2, 0) التعبير (مدمجة) فاصلة المشغل الذي يقيم اثنين من التعبيرات الفرعية من أجل (أي، 1.2 أولا ثم 0)، يتجاهل القيمة والنتائج الأولى في ثانية واحدة. وبالتالي،

int x = (1.2, 0);

وما يعادل

int x = 0;

ووضع هذا جنبا إلى جنب مع decltype أن يعطي decltype(1.2, 0) يعني int. لا يوجد شيء خاص حقا عن 1.2 أو double هنا. على سبيل المثال، true ديه نوع bool وdecltype(true, 0) يعني int كذلك.

وماذا عن نوع الدرجة؟ لinstace، ما يعني decltype(f, 0)؟ فمن الطبيعي أن نتوقع أن هذا لا يزال يعني int ولكن قد لا يكون الأمر كذلك. في الواقع، قد يكون هناك الزائد للمشغل فاصلة مماثلة لfunc وظيفة سبق أن يأخذ foo وint وإرجاع char. في هذه الحالة، decltype(foo, 0) هو char.

وكيف يمكننا تجنب استخدام الزائد للمشغل فاصلة؟ حسنا، ليس هناك طريقة لتفرط في مشغل فاصلة لالمعامل void، ونحن يمكن أن يلقي أي شيء لvoid. ولذلك، decltype((void) f, 0) يعني int. في الواقع، (void) f يلقي f من foo إلى void التي لا أساسا لا شيء سوى القول إن التعبير يجب النظر في وجود نوع void. ثم يتم استخدام المدمج في فاصلة المشغل والنتائج ((void) f, 0) في 0 التي لديها نوع int. وبالتالي، decltype((void) f, 0) يعني int.

هل هذا يلقي ضروري حقا؟ حسنا، اذا لم يكن هناك الزائد للمشغل فاصلة أخذ foo وint ثم هذا ليس ضروريا. يمكننا أن تفقد دائما شفرة المصدر لمعرفة ما إذا كان هناك مثل هذا المشغل أو لا. ومع ذلك، إذا كان هذا يبدو في قالب وf ديه اكتب V وهي معلمة قالب، ثم انه لم يعد واضحا (أو حتى من المستحيل معرفة) ما إذا كان هذا الحمل الزائد للمشغل فاصلة موجودا أم لا. لتكون عامة نحن يلقي على أي حال.

وخلاصة القول: decltype((void) f, 0) هو وسيلة يتوهم القول int

.

و5) SFINAE:

وهذا هو العلم كله ؛-) OK أنا exagerating ولكنها ليست بسيطة جدا أيضا. ولذا فإنني سوف تبقي التفسير إلى الحد الأدنى.

وSFINAE لتقف على عدم تبديل ليس خطأ. وهذا يعني أنه عندما يتم استبدال معلمة قالب من نوع، قد تظهر رمز غير قانوني C ++، ولكن في بعض circunstances ، بدلا من اجهاض تجميع المترجم تتجاهل ببساطة رمز المخالف كما لو أنه لم يكن هناك . دعونا نرى كيف ينطبق على حالتنا:

// Primary template
template <typename T, typename U = int>
struct HasX : std::false_type { };

// Specialization for U = int
template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

وهنا، مرة أخرى، decltype((void) T::x, 0) هو وسيلة يتوهم القول int ولكن مع الاستفادة من SFINAE.

وعندما يتم استبداله T مع نوع، قد تظهر بناء غير صالح. على سبيل المثال، bool::x غير صالح C ++، بحيث يستبدل T مع bool في T::x ينتج لبناء صالح. وفقا لمبدأ SFINAE، المترجم لا يرفض قانون، فإنه يتجاهل ببساطة (أجزاء) عليه. بتعبير أدق، كما لدينا seenHasX<bool> يعني في الواقع HasX<bool, int>. وينبغي اختيار التخصص لU = int ولكن، في حين instantiating ذلك، المترجم يجد bool::x ويتجاهل التخصص قالب تماما كما لو أنها لم تكن موجودة.

وعند هذه النقطة، رمز essencially نفسها كما في حالة (2) أعلاه حيث يوجد فقط القالب الأساسي. وبالتالي، HasX<bool, int>::value == false.

والحجة نفسها التي استخدمت لbool يحمل لB منذ B::x هو بناء غير صالح (B لا يوجد لديه x الأعضاء). ومع ذلك، A::x على ما يرام والمترجم لا يرى مشكلة في instantiating التخصص لU = int (أو بتعبير أدق، لU = decltype((void) A::x, 0)). وبالتالي، HasX<A>::value == true.

و6) Unnaming U:

حسنا، والنظر في التعليمات البرمجية في (5) مرة أخرى، ونحن نرى أن U اسم لا يستخدم أي مكان ولكن في إعلانها (typename U). يمكننا ثم unname الحجة قالب الثانية، ونحن الحصول على الرمز المبين في الجزء العلوي من هذا المنصب.

وحصلت على توجيهك هنا من سؤال href="https://stackoverflow.com/questions/14522939/check-whether-a-field-exists-in-a-struct"> التي لديها أغلقت على أنه مكرر من هذا واحد. وأنا أعلم أنه في موضوع القديم، ولكن أردت فقط أن اقتراح بديل (بساطة؟) التنفيذ التي تعمل مع C ++ 11. لنفترض أننا نريد أن تحقق ما إذا كان فئة معينة لها متغير عضو يسمى id:

#include <type_traits>

template<typename T, typename = void>
struct has_id : std::false_type { };

template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };

وهذا كل شيء. وهنا هو كيف سيتم استخدامه ( <م> مثال حي ) :

#include <iostream>

using namespace std;

struct X { int id; };
struct Y { int foo; };

int main()
{
    cout << boolalpha;
    cout << has_id<X>::value << endl;
    cout << has_id<Y>::value << endl;
}

والأمور يمكن أن تكون أكثر بساطة مع اثنين من وحدات الماكرو:

#define DEFINE_MEMBER_CHECKER(member) \
    template<typename T, typename V = bool> \
    struct has_ ## member : false_type { }; \
    template<typename T> \
    struct has_ ## member<T, \
        typename enable_if< \
            !is_same<decltype(declval<T>().member), void>::value, \
            bool \
            >::type \
        > : true_type { };

#define HAS_MEMBER(C, member) \
    has_ ## member<C>::value

والتي يمكن أن تستخدم هذه الطريقة:

using namespace std;

struct X { int id; };
struct Y { int foo; };

DEFINE_MEMBER_CHECKER(foo)

int main()
{
    cout << boolalpha;
    cout << HAS_MEMBER(X, foo) << endl;
    cout << HAS_MEMBER(Y, foo) << endl;
}

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

فيما يلي بعض مقتطفات الاستخدام:*الشجاعة في كل هذا تقع في الأسفل

التحقق من وجود عضو x في فئة معينة.يمكن أن يكون var أو func أو class أو union أو enum:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

التحقق من وظيفة العضو void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

التحقق من وجود متغير العضو x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

التحقق من فئة الأعضاء x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

التحقق من اتحاد الأعضاء x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

التحقق من وجود تعداد الأعضاء x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

التحقق من وجود أي وظيفة عضو x بغض النظر عن التوقيع:

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

أو

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

التفاصيل والأساسية:

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

template <typename... Args> struct ambiguate : public Args... {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

وحدات الماكرو (إل ديابلو!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

CREATE_MEMBER_CHECKS:

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)

Boost.ConceptTraits تقدم بين الآخرين بعض وحدات الماكرو ل تحديد نوع الصفات، وعلى سبيل المثال BOOST_TT_EXT_DEFINE_HAS_MEMBER(name)، الذي يحدد سمة نوع من النموذج:

has_member_##name<T>

ويعطي هذا صحيح إذا T لديه نوع عضو يسمى. ولكن لاحظ أن هذا لن كشف أعضاء نوع مرجع.

في حال كنت سوف يكون كافيا لإضافة في ملف الرأس

BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)

ووتحقق على النحو التالي

BOOST_STATIC_ASSERT(has_member_x<P1>::value);

وهذه التقنية المستخدمة هي نفس شرح واحد على بعض الإجابات السابقة.

ولسوء الحظ ليس أكثر الحفاظ على هذه المكتبة. والآن بعد أن C ++ 0X سوف لا يشمل مفهوم، هذا إلى جانب مكتبة SFINAE هي البديل الأمثل للعمل مع معظم المفاهيم.

لماذا لا يتم استخدام التخصص مثل هذا:

struct P1 {int x; };
struct P2 {int X; };

template<class P> 
bool Check_x(P p) { return true; }

template<> 
bool Check_x<P2>(P2 p) { return false; }

والجواب الثاني (litb) لهذا يوضح كيفية كشف عضوا:

هل من الممكن أن يكتب قالب للتحقق من أجل وجود وظيفة في؟

لماذا لا يمكنك فقط إنشاء تخصصات قالب Check_x؟

template<> bool Check_x(P1 p) { return true; }
template<> bool Check_x(P2 p) { return false; }

وهيك، عندما أفكر في ذلك. إذا كان لديك فقط نوعين، لماذا تحتاج حتى قوالب لهذا؟

هل وظائف (س، X، Y، Y) من الفئة الأساسية المجردة، أو يمكن أن تكون إعادة بناء التعليمات أن تكون كذلك؟ إذا كان الأمر كذلك يمكنك استخدام الماكرو SUPERSUBCLASS () من حديث تصميم C ++، جنبا إلى جنب مع الأفكار من الإجابة على هذا السؤال:

تجميع الوقت نوع القائمة على إرسال

ويمكن أن نحصل في وقت الترجمة: 0 - not_member, 1 - is_object, 2 - is_function لكل فئة المطلوبة وعضو - الكائن أو وظيفة: http://ideone.com / Fjm9u5

#include <iostream>
#include <type_traits>

#define IS_MEMBER(T1, M)    \
struct {        \
    struct verystrangename1 { bool M; };    \
    template<typename T> struct verystrangename2 : verystrangename1, public T { }; \
    \
    enum return_t { not_member, is_object, is_function }; \
    template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member;  }  \
    template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; }   \
    template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; }   \
    constexpr operator return_t() { return what_member<T1>(); } \
}

struct t {
    int aaa;
    float bbb;
    void func() {}
};

// Can't be in function
IS_MEMBER(t, aaa) is_aaa_member_of_t;
IS_MEMBER(t, ccc) is_ccc_member_of_t;
IS_MEMBER(t, func) is_func_member_of_t;

// known at compile time
enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t };
static constexpr int const_is_func_member_of_t = is_func_member_of_t;

int main() {        
    std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" <<
        "is aaa member of t = " << is_aaa_member_of_t << std::endl << 
        "is ccc member of t = " << is_ccc_member_of_t << std::endl << 
        "is func member of t = " << is_func_member_of_t << std::endl << 
        std::endl;

    return 0;
}

والنتيجة:

0 - not_member, 1 - is_object, 2 - is_function 

is aaa member of t = 1
is ccc member of t = 0
is func member of t = 2

لفئة / البنية:

struct t {
    int aaa;
    float bbb;
    void func() {}
};
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top