سؤال

لسبب ما اعتقدت C ++ 0x المسموح به std::initializer_list كوسيطة وظيفة للوظائف التي تتوقع أنواعًا يمكن بناؤها من هذا القبيل ، على سبيل المثال std::vector. ولكن على ما يبدو ، لا يعمل. هل هذا مجرد برنامج التحويل البرمجي الخاص بي ، أم لن يعمل هذا أبدًا؟ هل بسبب مشاكل حل الحمل الزائد المحتملة؟

#include <string>
#include <vector>

void function(std::vector<std::string> vec)
{
}

int main()
{
    // ok
    std::vector<std::string> vec {"hello", "world", "test"};

    // error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
    function( {"hello", "world", "test"} );
}
هل كانت مفيدة؟

المحلول

GCC لديه خطأ. المعيار يجعل هذا صالحا. نرى:

لاحظ أن هناك جانبان من هذا

  • كيف تتم التهيئة بشكل عام؟
  • كيف يتم استخدام التهيئة أثناء دقة الحمل الزائد ، وما هي التكلفة التي لديها؟

تم الإجابة على السؤال الأول في القسم 8.5. تم الإجابة على السؤال الثاني في القسم 13.3. على سبيل المثال ، يتم التعامل مع الربط المرجعي في 8.5.3 و 13.3.3.1.4, ، بينما يتم التعامل مع تهيئة القائمة في 8.5.4 و 13.3.3.1.5.

8.5/14,16:

التهيئة التي تحدث في النموذج

T x = a;

وكذلك في الحجة المارة, ، يتم إرجاع الوظيفة ، ورمي استثناء (15.1) ، والتعامل مع استثناء (15.3) ، وتهيئة العضو الكلي (8.5.1) تسمى النسخ.
.
.
دلالات المهيئات هي كما يلي [...]: إذا كان المُعزّر عبارة عن قائمة متداخلة ، فإن الكائن قد تم اختياريه (8.5.4).

عند النظر في المرشح function, ، سيشاهد المترجم قائمة المهيمنة (التي لا يوجد بها نوع حتى الآن - إنها مجرد بناء نحوي!) كحجة ، و std::vector<std::string> كمعلمة من function. لمعرفة تكلفة التحويل وما إذا كنا يستطيع تحويلها في سياق التحميل الزائد ، 13.3.3.1/5 يقول

13.3.3.1.5/1:

عندما تكون الوسيطة قائمة مُهيئ (8.5.4) ، فهي ليست تعبيرًا وقواعد خاصة تنطبق على تحويلها إلى نوع معلمة.

13.3.3.1.5/3:

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

فئة غير مجمعة X هو std::vector<std::string>, ، وسأكتشف أفضل مُنشئ واحد أدناه. القاعدة الأخيرة تمنحنا استخدام التحويلات المحددة للمستخدم في حالات مثل ما يلي:

struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }

يُسمح لنا بتحويل السلسلة الحرفية إلى std::string, ، حتى لو كان هذا يحتاج إلى تحويل محدد للمستخدم. ومع ذلك ، فإنه يشير إلى قيود فقرة أخرى. ماذا فعلت 13.3.3.1 قل؟

13.3.3.1/4, ، وهي الفقرة المسؤولة عن منع التحويلات المحددة للمستخدم المتعددة. سننظر فقط في تهيئة القائمة:

ومع ذلك ، عند النظر في وسيطة وظيفة تحويل المستخدم [(أو المُنشئ)] وهو مرشح بواسطة [...] ويتم اعتبار التحويل إلى بعض الفئة X أو الإشارة إلى (ربما cv-quali ed) x للمعلمة الأولى من مُنشئ X ، أو [...] ، فقط تسلسل التحويل القياسي وتسلسلات تحويل القطع النارية.

لاحظ أن هذا هو التقييد المهم: إذا لم يكن الأمر كذلك ، فيمكن لما ورد أعلاه استخدام البناء البني لإنشاء تسلسل تحويل جيد على قدم المساواة ، وسيكون التهيئة غامضة. (لاحظ الالتباس المحتمل لـ "A أو B و C" في هذه القاعدة: من المفترض أن أقول "(A أو B) و C" - لذلك نحن مقيدون فقط عند محاولة التحويل بواسطة مُنشئ من X وجود معلمة من النوع X).

نحن مفوضون ل 13.3.1.7 لجمع المُنشئين ، يمكننا استخدامه للقيام بهذا التحويل. دعنا نتعامل مع هذه الفقرة من الجانب العام تبدأ من 8.5 الذي فوضنا إلى 8.5.4:

8.5.4/1:

يمكن أن يحدث تنصيب القائمة في سياقات التحويل المباشر أو النسخ ؛ يتم استدعاء القائمة في سياق تخصيص مباشر القائمة المباشرة ويتم استدعاء تنصيب القائمة في سياق نسخ تخصيص نسخ القائمة.

8.5.4/2:

المنشئ هو مُنشئ قائمة التهيئة إذا كانت المعلمة الأولى من النوع std::initializer_list<E> أو الإشارة إلى CV-Quali std::initializer_list<E> بالنسبة لبعض نوع E ، وإما لا توجد معلمات أخرى وإلا فإن جميع المعلمات الأخرى لها وسيط افتراضية (8.3.6).

8.5.4/3:

يتم تحديد القائمة الخاصة بكائن أو مرجع من النوع T على النحو التالي: [...] وإلا ، إذا كان t هو نوع فئة ، يتم النظر في المُنشئين. إذا كان لدى T مُنشئ قائمة التهيئة ، فإن قائمة الوسيطة تتكون من قائمة التهيئة كوسيطة واحدة ؛ خلاف ذلك ، تتكون قائمة الوسيطة من عناصر قائمة التهيئة. يتم تعداد المُنشئين المعمول بها (13.3.1.7) ويتم اختيار أفضل واحد من خلال دقة التحميل الزائد (13.3).

في هذا الوقت، T هو نوع الفصل std::vector<std::string>. لدينا حجة واحدة (التي لا يوجد بها نوع حتى الآن! نحن فقط في سياق وجود قائمة مُهيئة نحوية). يتم تعداد المُنشئين اعتبارًا من 13.3.1.7:

...] إذا كان T لديه مُنشئ قائمة التهيئة (8.5.4) ، تتكون قائمة الوسيطة من قائمة التهيئة كوسيطة واحدة ؛ خلاف ذلك ، تتكون قائمة الوسيطة من عناصر قائمة التهيئة. بالنسبة لنسخ قائمة النسخ ، فإن وظائف المرشح هي جميع مُنشئات T. ومع ذلك ، إذا تم اختيار مُنشئ صريح ، فإن التهيئة غير متشددة.

سننظر فقط في قائمة التهيئة لـ std::vector بصفته المرشح الوحيد ، نظرًا لأننا نعرف بالفعل أن الآخرين لن يفوزوا عليه أو لا يناسب الحجة. لديها التوقيع التالي:

vector(initializer_list<std::string>, const Allocator& = Allocator());

الآن ، قواعد تحويل قائمة التهيئة إلى std::initializer_list<T> (لتصنيف تكلفة تحويل الوسيطة/المعلمة) يتم تعدادها في 13.3.3.1.5:

عندما تكون الوسيطة قائمة مُهيئ (8.5.4) ، فهي ليست تعبيرًا وقواعد خاصة تنطبق على تحويلها إلى نوع معلمة. [...] إذا كان نوع المعلمة هو std::initializer_list<X> ويمكن تحويل جميع عناصر قائمة التهيئة ضمنيًا إلى X ، وهو تسلسل التحويل الضمني هو أسوأ تحويل ضروري لتحويل عنصر من القائمة إلى X. يمكن أن يكون هذا التحويل بمثابة تحويل مستخدم حتى في سياق دعوة إلى مُنشئ قائمة التهيئة.

الآن ، سيتم تحويل قائمة التهيئة بنجاح ، وتسلسل التحويل هو تحويل محدد للمستخدم (من char const[N] إلى std::string). كيف يتم صنع هذا مفصل في 8.5.4 تكرارا:

خلاف ذلك ، إذا كان T هو تخصص std::initializer_list<E>, ، يتم إنشاء كائن initializer_list كما هو موضح أدناه ويستخدم لتهيئة الكائن وفقًا لقواعد تهيئة كائن من فئة من نفس النوع (8.5). (...)

نرى 8.5.4/4 كيف يتم اتخاذ هذه الخطوة الأخيرة :)

نصائح أخرى

يبدو أنه يعمل بهذه الطريقة:

function( {std::string("hello"), std::string("world"), std::string("test")} );

ربما يكون هذا خطأ في البرمجيات ، لكن ربما تطلب الكثير من التحويلات الضمنية.

غير متأكد من ذلك ، لكنني أظن أن ما يجري هنا هو أن التحويل إلى unitializer_list هو تحويل واحد ، وتحويل ذلك إلى المتجه هو تحويل آخر. إذا كان هذا هو الحال ، فأنت تتجاوز حد تحويل واحد ضمني واحد فقط ...

هذا إما حشرة البرمجة أو المترجم الخاص بك لا يدعم STD :: Emtructionizer_list. تم اختباره على GCC 4.5.1 وتجمع بشكل جيد.

يجب عليك تحديد نوع من intirlizer_list

function(std::initializer_list<std::string>{"hello", "world", "test"} );

حظا طيبا وفقك الله

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