Sigslot عبارة عن تطبيق آمن للرأس فقط وفتحات الإشارة لـ C++.
كان الهدف الرئيسي هو استبدال Boost.Signals2.
وبصرف النظر عن الميزات المعتادة، فإنه يقدم
تم اختبار Sigslot بواسطة الوحدة ويجب أن تكون موثوقة ومستقرة بدرجة كافية لتحل محل Boost Signals2.
يتم إجراء الاختبارات بشكل نظيف ضمن العنوان والخيط ومعقمات السلوك غير المحددة.
تسمح العديد من التطبيقات بأنواع إرجاع الإشارة، لكن Sigslot لا يسمح بذلك لأنني لا أستخدمها. وإذا اقتنعت بغير ذلك فقد أغير رأيي فيما بعد.
لا يلزم تجميع أو تثبيت، فقط قم بتضمين sigslot/signal.hpp
واستخدمه. يعتمد Sigslot حاليًا على مترجم متوافق مع C++ 14، ولكن إذا لزم الأمر، فقد يتم تحديثه وتحديثه إلى C++ 11. من المعروف أنه يعمل مع المجمعين Clang 4.0 وGC 5.0+ على GNU Linux وMSVC 2017 والإصدارات الأحدث وClang-cl وMinGW على Windows.
ومع ذلك، كن على دراية بوجود مشكلة محتملة على Windows مع مترجمي MSVC وClang-Cl، والتي قد تحتاج إلى إشارات رابط /OPT:NOICF
في حالات استثنائية. اقرأ فصل تفاصيل التنفيذ للحصول على شرح.
يتم توفير ملف قائمة CMake لغرض التثبيت وإنشاء وحدة استيراد CMake. هذه هي طريقة التثبيت المفضلة. الهدف Pal::Sigslot
المستورد متاح ويطبق بالفعل علامات الرابط المطلوبة. وهو مطلوب أيضًا للأمثلة والاختبارات، والتي تعتمد اختياريًا على Qt5 وBoost لاختبارات وحدة المحولات.
# Using Sigslot from cmake
find_package (PalSigslot)
add_executable (MyExe main.cpp)
target_link_libraries (MyExe PRIVATE Pal::Sigslot)
يتوفر خيار التكوين SIGSLOT_REDUCE_COMPILE_TIME
في وقت التكوين. عند التنشيط، فإنه يحاول تقليل تضخم التعليمات البرمجية عن طريق تجنب إنشاء مثيلات القالب الثقيلة الناتجة عن استدعاءات std::make_shared
. يتم إيقاف هذا الخيار بشكل افتراضي، ولكن يمكن تفعيله لأولئك الذين يرغبون في تفضيل حجم التعليمات البرمجية ووقت الترجمة على نطاق تعليمات برمجية أقل كفاءة قليلاً.
يمكن إجراء التثبيت باستخدام الإرشادات التالية من الدليل الجذر:
mkdir build && cd build
cmake .. -DSIGSLOT_REDUCE_COMPILE_TIME=ON -DCMAKE_INSTALL_PREFIX= ~ /local
cmake --build . --target install
# If you want to compile examples:
cmake --build . --target sigslot-examples
# And compile/execute unit tests:
cmake --build . --target sigslot-tests
يمكن أيضًا دمج Pal::Sigslot
باستخدام طريقة FetchContent.
include (FetchContent)
FetchContent_Declare(
sigslot
GIT_REPOSITORY https://github.com/palacaze/sigslot
GIT_TAG 19a6f0f5ea11fc121fe67f81fd5e491f2d7a4637 # v1.2.0
)
FetchContent_MakeAvailable(sigslot)
add_executable (MyExe main.cpp)
target_link_libraries (MyExe PRIVATE Pal::Sigslot)
تطبق Sigslot بنية فتحة الإشارة الشائعة في أطر عمل واجهة المستخدم، مما يجعل من السهل استخدام نمط المراقب أو البرمجة القائمة على الأحداث. نقطة الدخول الرئيسية للمكتبة هي قالب الفئة sigslot::signal<T...>
.
الإشارة هي كائن يمكنه إرسال إشعارات مكتوبة، وقيم محددة بالفعل بعد معلمات قالب فئة الإشارة، وتسجيل أي عدد من معالجات الإشعارات (القابلة للاستدعاء) لأنواع الوسيطات المتوافقة ليتم تنفيذها بالقيم المقدمة كلما حدث انبعاث إشارة. في لغة فتحة الإشارة، يسمى هذا توصيل فتحة بإشارة، حيث تمثل "الفتحة" مثيلًا قابلاً للاستدعاء ويمكن اعتبار "الاتصال" بمثابة رابط مفاهيمي من الإشارة إلى الفتحة.
جميع المقتطفات المعروضة أدناه متاحة في شكل كود مصدر قابل للتجميع في الدليل الفرعي للمثال.
فيما يلي المثال الأول الذي يعرض أهم الميزات الأساسية للمكتبة.
نعلن أولاً عن sig
إشارة خالية من المعلمات، ثم نواصل توصيل عدة فتحات وأخيرًا نصدر إشارة تؤدي إلى استدعاء كل فتحة قابلة للاستدعاء متصلة مسبقًا. لاحظ كيف تتعامل المكتبة مع أشكال متنوعة من العناصر القابلة للاستدعاء.
# include < sigslot/signal.hpp >
# include < iostream >
void f () { std::cout << " free function n " ; }
struct s {
void m () { std::cout << " member function n " ; }
static void sm () { std::cout << " static member function n " ; }
};
struct o {
void operator ()() { std::cout << " function object n " ; }
};
int main () {
s d;
auto lambda = []() { std::cout << " lambda n " ; };
auto gen_lambda = []( auto && ... a ) { std::cout << " generic lambda n " ; };
// declare a signal instance with no arguments
sigslot:: signal <> sig;
// connect slots
sig. connect (f);
sig. connect (&s::m, &d);
sig. connect (&s::sm);
sig. connect ( o ());
sig. connect (lambda);
sig. connect (gen_lambda);
// a free connect() function is also available
sigslot::connect (sig, f);
// emit a signal
sig ();
}
بشكل افتراضي، يكون ترتيب استدعاء الفتحة عند إرسال الإشارة غير محدد، يرجى عدم الاعتماد على كونه هو نفسه دائمًا. يمكنك تقييد أمر استدعاء معين باستخدام مجموعات الفتحات، والتي سيتم عرضها لاحقًا.
كان هذا المثال الأول بسيطًا ولكنه ليس مفيدًا جدًا، فلننتقل إلى الإشارة التي تبعث قيمًا بدلاً من ذلك. يمكن للإشارة أن تصدر أي عدد من الوسائط، أدناه.
# include < sigslot/signal.hpp >
# include < iostream >
# include < string >
struct foo {
// Notice how we accept a double as first argument here.
// This is fine because float is convertible to double.
// 's' is a reference and can thus be modified.
void bar ( double d, int i, bool b, std::string &s) {
s = b ? std::to_string (i) : std::to_string (d);
}
};
// Function objects can cope with default arguments and overloading.
// It does not work with static and member functions.
struct obj {
void operator ()( float , int , bool , std::string &, int = 0 ) {
std::cout << " I was here n " ;
}
void operator ()() {}
};
int main () {
// declare a signal with float, int, bool and string& arguments
sigslot:: signal < float , int , bool , std::string&> sig;
// a generic lambda that prints its arguments to stdout
auto printer = [] ( auto a, auto && ... args ) {
std::cout << a;
( void )std::initializer_list< int >{
(( void )(std::cout << " " << args), 1 )...
};
std::cout << " n " ;
};
// connect the slots
foo ff;
sig. connect (printer);
sig. connect (&foo::bar, &ff);
sig. connect ( obj ());
float f = 1 . f ;
short i = 2 ; // convertible to int
std::string s = " 0 " ;
// emit a signal
sig (f, i, false , s);
sig (f, i, true , s);
}
كما هو موضح، لا يلزم أن تكون أنواع وسيطات الفتحات متطابقة تمامًا مع معلمات قالب الإشارة، فمن الجيد أن تكون قابلة للتحويل. تعد الوسائط العامة جيدة أيضًا، كما هو موضح في printer
lambda العامة (والتي كان من الممكن كتابتها كقالب دالة أيضًا).
يوجد حاليًا قيدان يمكنني التفكير فيهما فيما يتعلق بالمعالجة القابلة للاستدعاء: الوسائط الافتراضية والتحميل الزائد للوظيفة. كلاهما يعمل بشكل صحيح في حالة الكائنات الوظيفية ولكن سيفشل في الترجمة مع الوظائف الثابتة والعضوية، لأسباب مختلفة ولكن ذات صلة.
خذ بعين الاعتبار الجزء التالي من التعليمات البرمجية:
struct foo {
void bar ( double d);
void bar ();
};
ما الذي يجب أن يشير إليه &foo::bar
؟ وفقًا للتحميل الزائد، لا يتم تعيين هذا المؤشر فوق وظيفة العضو إلى رمز فريد، لذلك لن يتمكن المترجم من اختيار الرمز الصحيح. تتمثل إحدى طرق حل الرمز الصحيح في توجيه مؤشر الوظيفة بشكل صريح إلى نوع الوظيفة الصحيح. فيما يلي مثال يفعل ذلك باستخدام أداة مساعدة صغيرة لبناء جملة أخف (في الحقيقة ربما سأضيف هذا إلى المكتبة قريبًا).
# include < sigslot/signal.hpp >
template < typename ... Args, typename C>
constexpr auto overload ( void (C::*ptr)(Args...)) {
return ptr;
}
template < typename ... Args>
constexpr auto overload ( void (*ptr)(Args...)) {
return ptr;
}
struct obj {
void operator ()( int ) const {}
void operator ()() {}
};
struct foo {
void bar ( int ) {}
void bar () {}
static void baz ( int ) {}
static void baz () {}
};
void moo ( int ) {}
void moo () {}
int main () {
sigslot:: signal < int > sig;
// connect the slots, casting to the right overload if necessary
foo ff;
sig. connect (overload< int >(&foo::bar), &ff);
sig. connect (overload< int >(&foo::baz));
sig. connect (overload< int >(&moo));
sig. connect ( obj ());
sig ( 0 );
return 0 ;
}
الوسائط الافتراضية ليست جزءًا من توقيع نوع الوظيفة، ويمكن إعادة تعريفها، لذا يصعب التعامل معها حقًا. عند توصيل فتحة بإشارة، تحدد المكتبة ما إذا كان من الممكن استدعاء المادة القابلة للاستدعاء مع أنواع وسائط الإشارة، ولكن في هذه المرحلة يكون وجود وسائط الوظيفة الافتراضية غير معروف، لذا قد يكون هناك عدم تطابق في عدد الوسائط.
الحل البسيط لحالة الاستخدام هذه هو إنشاء محول ربط، في الواقع يمكننا جعله عامًا تمامًا كما يلي:
# include < sigslot/signal.hpp >
# define ADAPT ( func )
[=]( auto && ...a) { (func)(std::forward< decltype (a)>(a)...); }
void foo ( int &i, int b = 1 ) {
i += b;
}
int main () {
int i = 0 ;
// fine, all the arguments are handled
sigslot:: signal < int &, int > sig1;
sig1. connect (foo);
sig1 (i, 2 );
// must wrap in an adapter
i = 0 ;
sigslot:: signal < int &> sig2;
sig2. connect ( ADAPT (foo));
sig2 (i);
return 0 ;
}
ما لم يتم توضيحه حتى الآن هو أن signal::connect()
يُرجع فعليًا كائن sigslot::connection
الذي يمكن استخدامه لإدارة سلوك وعمر اتصال فتحة الإشارة. sigslot::connection
هو كائن خفيف الوزن (في الأساس std::weak_ptr
) يسمح بالتفاعل مع اتصال فتحة الإشارة المستمر ويكشف عن الميزات التالية:
signal::connect()
. لا يربط sigslot::connection
الاتصال بالنطاق: هذا ليس كائن RAII، وهو ما يفسر سبب إمكانية نسخه. ومع ذلك، يمكن تحويله ضمنيًا إلى sigslot::scoped_connection
الذي يدمر الاتصال عند الخروج عن النطاق.
وفيما يلي مثال يوضح بعض هذه الميزات:
# include < sigslot/signal.hpp >
# include < string >
int i = 0 ;
void f () { i += 1 ; }
int main () {
sigslot:: signal <> sig;
// keep a sigslot::connection object
auto c1 = sig. connect (f);
// disconnection
sig (); // i == 1
c1. disconnect ();
sig (); // i == 1
// scope based disconnection
{
sigslot::scoped_connection sc = sig. connect (f);
sig (); // i == 2
}
sig (); // i == 2;
// connection blocking
auto c2 = sig. connect (f);
sig (); // i == 3
c2. block ();
sig (); // i == 3
c2. unblock ();
sig (); // i == 4
}
يدعم Sigslot توقيع فتحة ممتدة مع مرجع sigslot::connection
إضافي كوسيطة أولى، مما يسمح بإدارة الاتصال من داخل الفتحة. يمكن الوصول إلى هذا التوقيع الموسع باستخدام طريقة connect_extended()
.
# include < sigslot/signal.hpp >
int main () {
int i = 0 ;
sigslot:: signal <> sig;
// extended connection
auto f = []( auto &con) {
i += 1 ; // do work
con. disconnect (); // then disconnects
};
sig. connect_extended (f);
sig (); // i == 1
sig (); // i == 1 because f was disconnected
}
يجب على المستخدم التأكد من أن عمر الفتحة يتجاوز عمر الإشارة، الأمر الذي قد يصبح مملاً في البرامج المعقدة. لتبسيط هذه المهمة، يمكن لـ Sigslot قطع اتصال كائن الفتحة الذي يمكنه تتبع عمره تلقائيًا. وللقيام بذلك، يجب أن تكون الفتحة قابلة للتحويل إلى مؤشر ضعيف بشكل ما.
يتم دعم std::shared_ptr
و std::weak_ptr
خارج الصندوق، ويتم توفير محولات لدعم boost::shared_ptr
و boost::weak_ptr
وQt QSharedPointer
و QWeakPointer
وأي فئة مشتقة من QObject
.
يمكن إضافة كائنات أخرى قابلة للتتبع عن طريق الإعلان عن وظيفة محول to_weak()
.
# include < sigslot/signal.hpp >
# include < sigslot/adapter/qt.hpp >
int sum = 0 ;
struct s {
void f ( int i) { sum += i; }
};
class MyObject : public QObject {
Q_OBJECT
public:
void add ( int i) const { sum += i; }
};
int main () {
sum = 0 ;
signal < int > sig;
// track lifetime of object and also connect to a member function
auto p = std::make_shared<s>();
sig. connect (&s::f, p);
sig ( 1 ); // sum == 1
p. reset ();
sig ( 1 ); // sum == 1
// track an unrelated object lifetime
struct dummy ;
auto l = [&]( int i) { sum += i; };
auto d = std::make_shared<dummy>();
sig. connect (l, d);
sig ( 1 ); // sum == 2
d. reset ();
sig ( 1 ); // sum == 2
// track a QObject
{
MyObject o;
sig. connect (&MyObject::add, &o);
sig ( 1 ); // sum == 3
}
sig ( 1 ); // sum == 3
}
هناك طريقة أخرى لضمان قطع الاتصال التلقائي للمؤشر عبر فتحات وظائف الأعضاء وهي الوراثة بشكل صريح من sigslot::observer
أو sigslot::observer_st
. فالأولى آمنة الخيط، على عكس الأخيرة.
هنا مثال على الاستخدام.
# include < sigslot/signal.hpp >
int sum = 0 ;
struct s : sigslot::observer_st {
void f ( int i) { sum += i; }
};
struct s_mt : sigslot::observer {
~s_mt () {
// Needed to ensure proper disconnection prior to object destruction
// in multithreaded contexts.
this -> disconnect_all ();
}
void f ( int i) { sum += i; }
};
int main () {
sum = 0 ;
signal < int > sig;
{
// Lifetime of object instance p is tracked
s p;
s_mt pm;
sig. connect (&s::f, &p);
sig. connect (&s_mt::f, &pm);
sig ( 1 ); // sum == 2
}
// The slots got disconnected at instance destruction
sig ( 1 ); // sum == 2
}
قد تكون الكائنات التي تستخدم هذا الأسلوب التدخلي متصلة بأي عدد من الإشارات غير ذات الصلة.
تم تقديم دعم لفصل الفتحة عن طريق توفير توقيع وظيفة مناسب أو مؤشر كائن أو متعقب في الإصدار 1.2.0.
يمكن للمرء فصل أي عدد من الفتحات باستخدام signal::disconnect()
، التي تقترح 4 أحمال زائدة لتحديد معيار قطع الاتصال:
لا يكون قطع اتصال lambdas ممكنًا إلا بالنسبة إلى lambdas المرتبطة بمتغير، نظرًا لتفردها.
يحتاج التحميل الزائد الثاني حاليًا إلى قطع اتصال RTTI من المؤشرات إلى وظائف الأعضاء والكائنات الوظيفية و lambdas. لا ينطبق هذا القيد على وظائف الأعضاء الحرة والثابتة. تنبع الأسباب من حقيقة أنه في C++، لا يمكن مقارنة المؤشرات إلى وظائف الأعضاء من الأنواع غير المرتبطة، على عكس المؤشرات إلى وظائف الأعضاء الحرة والثابتة. على سبيل المثال، يمكن أن يكون للمؤشر إلى وظائف الأعضاء في الأساليب الافتراضية لفئات مختلفة نفس العنوان (يقومون بتخزين إزاحة الطريقة في الجدول vtable).
ومع ذلك، يمكن تجميع Sigslot مع تعطيل RTTI وسيتم إلغاء تنشيط التحميل الزائد في الحالات التي بها مشكلات.
باعتبارها عقدة جانبية، من المسلم به أن هذه الميزة أضافت تعليمات برمجية أكثر مما كان متوقعًا في البداية لأنها صعبة وسهلة الخطأ. لقد تم تصميمه بعناية، مع أخذ الصحة في الاعتبار، وليس له أي تكاليف خفية إلا إذا كنت تستخدمه بالفعل.
فيما يلي مثال يوضح الميزة.
# include < sigslot/signal.hpp >
# include < string >
static int i = 0 ;
void f1 () { i += 1 ; }
void f2 () { i += 1 ; }
struct s {
void m1 () { i += 1 ; }
void m2 () { i += 1 ; }
void m3 () { i += 1 ; }
};
struct o {
void operator ()() { i += 1 ; }
};
int main () {
sigslot:: signal <> sig;
s s1;
auto s2 = std::make_shared<s>();
auto lbd = [&] { i += 1 ; };
sig. connect (f1); // #1
sig. connect (f2); // #2
sig. connect (&s::m1, &s1); // #3
sig. connect (&s::m2, &s1); // #4
sig. connect (&s::m3, &s1); // #5
sig. connect (&s::m1, s2); // #6
sig. connect (&s::m2, s2); // #7
sig. connect (o{}); // #8
sig. connect (lbd); // #9
sig (); // i == 9
sig. disconnect (f2); // #2 is removed
sig. disconnect (&s::m1); // #3 and #6 are removed
sig. disconnect (o{}); // #8 and is removed
// sig.disconnect(&o::operator()); // same as the above, more efficient
sig. disconnect (lbd); // #9 and is removed
sig. disconnect (s2); // #7 is removed
sig. disconnect (&s::m3, &s1); // #5 is removed, not #4
sig (); // i == 11
sig. disconnect_all (); // remove all remaining slots
return 0 ;
}
بدءًا من الإصدار 1.2.0، يمكن تعيين معرف مجموعة للفتحات من أجل التحكم في الترتيب النسبي لاستدعاء الفتحات.
ترتيب استدعاء الفتحات في نفس المجموعة غير محدد ولا ينبغي الاعتماد عليه، ولكن يتم استدعاء مجموعات الفتحات بترتيب معرف المجموعة التصاعدي. عندما لا يتم تعيين معرف المجموعة للفتحة، يتم تعيينه للمجموعة 0. يمكن أن تحتوي معرفات المجموعة على أي قيمة في نطاق الأعداد الصحيحة 32 بت الموقعة.
# include < sigslot/signal.hpp >
# include < cstdio >
# include < limits >
int main () {
sigslot:: signal <> sig;
// simply assigning a group id as last argument to connect
sig. connect ([] { std::puts ( " Second " ); }, 1 );
sig. connect ([] { std::puts ( " Last " ); }, std::numeric_limits<sigslot::group_id>:: max ());
sig. connect ([] { std::puts ( " First " ); }, - 10 );
sig ();
return 0 ;
}
يمكن استخدام وظيفة sigslot::connect()
المستقلة لتوصيل إشارة بأخرى باستخدام الوسائط المتوافقة.
# include < sigslot/signal.hpp >
# include < iostream >
int main () {
sigslot:: signal < int > sig1;
sigslot:: signal < double > sig2;
sigslot::connect (sig1, sig2);
sigslot::connect (sig2, [] ( double d) { std::cout << " got " << d << std::endl; });
sig ( 1 );
return 0 ;
}
يتم اختبار سلامة الخيط بواسطة الوحدة. على وجه الخصوص، يعمل انبعاث الإشارة المتقاطعة والانبعاث العودي بشكل جيد في سيناريو مؤشرات الترابط المتعددة.
sigslot::signal
هو typedef لفئة قالب sigslot::signal_base
الأكثر عمومية، والتي يجب أن تكون وسيطة القالب الأولى الخاصة بها من النوع القابل للقفل. سيحدد هذا النوع سياسة القفل الخاصة بالفئة.
يقدم Sigslot 2 typedefs،
sigslot::signal
قابلة للاستخدام من سلاسل رسائل متعددة وتستخدم std::mutex باعتبارها قابلة للقفل. على وجه الخصوص، يعد الاتصال وفصل الاتصال والانبعاث وتنفيذ الفتحة آمنًا للخيوط. كما أنها آمنة مع انبعاث إشارة متكررة.sigslot::signal_st
بديلاً غير آمن للخيط، فهو يتاجر بالأمان من أجل تشغيل أسرع قليلاً. تعد مقارنة مؤشرات الوظائف بمثابة كابوس في C++. فيما يلي جدول يوضح حجم وعنوان مجموعة متنوعة من الحالات كعرض:
void fun () {}
struct b1 {
virtual ~b1 () = default ;
static void sm () {}
void m () {}
virtual void vm () {}
};
struct b2 {
virtual ~b2 () = default ;
static void sm () {}
void m () {}
virtual void vm () {}
};
struct c {
virtual ~c () = default ;
virtual void w () {}
};
struct d : b1 {
static void sm () {}
void m () {}
void vm () override {}
};
struct e : b1, c {
static void sm () {}
void m () {}
void vm () override {}
};
رمز | دول مجلس التعاون الخليجي 9 لينكس 64 حجم | دول مجلس التعاون الخليجي 9 لينكس 64 عنوان | إم إس في سي 16.6 32 حجم | إم إس في سي 16.6 32 عنوان | دول مجلس التعاون الخليجي 8 مينغو 32 حجم | دول مجلس التعاون الخليجي 8 مينغو 32 عنوان | كلانج-سي إل 9 32 حجم | كلانج-سي إل 9 32 عنوان |
---|---|---|---|---|---|---|---|---|
هزار | 8 | 0x802340 | 4 | 0x1311A6 | 4 | 0xF41540 | 4 | 0x0010AE |
&ب1::سم | 8 | 0xE03140 | 4 | 0x7612A5 | 4 | 0x308D40 | 4 | 0x0010AE |
&ب1::م | 16 | 0xF03240 | 4 | 0x1514A5 | 8 | 0x248D40 | 4 | 0x0010AE |
&b1::vm | 16 | 0x11 | 4 | 0x9F11A5 | 8 | 0x09 | 4 | 0x8023AE |
&ب2::سم | 8 | 0x003340 | 4 | 0xA515A5 | 4 | 0x408D40 | 4 | 0x0010AE |
&ب2::م | 16 | 0x103440 | 4 | 0xEB10A5 | 8 | 0x348D40 | 4 | 0x0010AE |
&b2::vm | 16 | 0x11 | 4 | 0x6A14A5 | 8 | 0x09 | 4 | 0x8023AE |
&د::سم | 8 | 0x203440 | 4 | 0x2612A5 | 4 | 0x108D40 | 4 | 0x0010AE |
&د::م | 16 | 0x303540 | 4 | 0x9D13A5 | 8 | 0x048D40 | 4 | 0x0010AE |
&د::vm | 16 | 0x11 | 4 | 0x4412A5 | 8 | 0x09 | 4 | 0x8023AE |
&ه::سم | 8 | 0x403540 | 4 | 0xF911A5 | 4 | 0x208D40 | 4 | 0x0010AE |
&ه::م | 16 | 0x503640 | 8 | 0x8111A5 | 8 | 0x148D40 | 8 | 0x0010AE |
&ه::vm | 16 | 0x11 | 8 | 0xA911A5 | 8 | 0x09 | 8 | 0x8023AE |
يعمل كل من MSVC وClang-cl في وضع الإصدار على تحسين الوظائف بنفس التعريف عن طريق دمجها. هذا هو السلوك الذي يمكن إلغاء تنشيطه باستخدام خيار رابط /OPT:NOICF
. تعتمد اختبارات وأمثلة Sigslot على الكثير من العناصر القابلة للاستدعاء المتطابقة التي تؤدي إلى هذا السلوك، ولهذا السبب تقوم بإلغاء تنشيط هذا التحسين المحدد على المترجمين المتأثرين.
يمكن أن يؤدي استخدام أجهزة lambda العامة ذات إصدار دول مجلس التعاون الخليجي الأقل من الإصدار 7.4 إلى ظهور الخطأ رقم 68071.