ما قدرات جديدة هل المعرفة من قبل المستخدم حرفية إضافة إلى C++?

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

سؤال

C++11 يدخل المعرفة من قبل المستخدم حرفية والتي سوف تسمح مقدمة من جديد الحرفي الجملة على أساس القائمة حرفية (int, hex, string, float) بحيث أن أي نوع سوف تكون قادرة على أن يكون الحرفي العرض.

أمثلة:

// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{ 
    return std::complex<long double>(0, d); 
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)

// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42

// std::string
std::string operator "" _s(const char* str, size_t /*length*/) 
{ 
    return std::string(str); 
}

auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer

// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

للوهلة الأولى يبدو هذا رائع جدا ولكن أنا أتساءل كيف ينطبق عليه حقا ، عندما حاولت التفكير في وجود اللواحق _AD و _BC إنشاء التواريخ وجدت أنه من إشكالية بسبب مشغل النظام. 1974/01/06_AD أولا تقييم 1974/01 (عادي ints) فقط في وقت لاحق 06_AD (ناهيك من آب / أغسطس و أيلول / سبتمبر بعد أن تكون مكتوبة بدون 0 لمدة ثماني الأسباب).هذا ويمكن عمل حول وجود جملة تكون 1974-1/6_AD بحيث المشغل تقييم النظام يعمل لكنه عالي الكعب.

فما سؤالي يتلخص في هذا ، هل تشعر هذه الميزة سوف تبرر نفسها ؟ ما حرفية تريد أن تحدد من شأنها أن تجعل C++ رمز أكثر للقراءة ؟


تحديث جملة تناسب المشروع النهائي في حزيران / يونيه 2011

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

المحلول

هنا هو حالة حيث هناك ميزة استخدام المعرفة من قبل المستخدم حرفية بدلا من دعوة منشئ:

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

ميزة هي أن وقت التشغيل استثناء يتم تحويلها إلى الترجمة الوقت الخطأ.لا يمكنك إضافة ثابتة تؤكد أن bitset المنشئ اتخاذ سلسلة (على الأقل ليس من دون سلسلة قالب الحجج).

نصائح أخرى

للوهلة الأولى, يبدو أن تكون بسيطة النحوية السكر.

ولكن عندما تبحث أعمق ، ونحن نرى أنه أكثر من السكر النحوية ، بل يمتد C++ المستخدم خيارات لإنشاء الأنواع المعرفة من قبل المستخدم التي تتصرف تماما مثل متميزة المدمج في أنواع. في هذا قليلا "مكافأة" هو مثيرة جدا للاهتمام C++11 بالإضافة إلى C++.

هل نحن حقا في حاجة إليها في C++?

أرى بعض الاستخدامات في مدونة كتبت في السنوات الماضية ، ولكن فقط لأنني لم أكن استخدامها في C++ لا يعني انها ليست مثيرة للاهتمام بالنسبة آخر C++ المطور.

كنا قد تستخدم في C++ (وفي ج ، أعتقد) ، مترجم-تعريف حرفية إلى نوع عدد صحيح أرقام قصيرة أو طويلة من الاعداد الصحيحه, أرقام حقيقية كما تطفو أو مزدوجة (أو حتى طويلة مزدوجة), و سلاسل الأحرف العادية أو واسعة حرف.

في C++, كان لدينا إمكانية إنشاء الخاصة بنا أنواع (أيفصول) يحتمل أن تكون مع أي النفقات العامة (مصدر ، إلخ.).كان لدينا إمكانية إضافة مشغلي أنواعها ، أن يكون لهم تتصرف مثل مماثلة المدمج في أنواع, التي تمكن C++ للمطورين استخدام المصفوفات و الأعداد المركبة بشكل طبيعي كما كانوا إذا هذه أضيفت إلى اللغة نفسها.حتى أننا يمكن أن تضيف يلقي المشغلين (التي عادة ما تكون فكرة سيئة ، ولكن في بعض الأحيان انها مجرد الحق في حل).

لقد غاب عن شيء واحد أن يكون المستخدم أنواع التصرف كما المدمج في أنواع:المعرفة من قبل المستخدم حرفية.

لذا أعتقد أن هذا التطور الطبيعي للغة ، ولكن أن تكون كاملة بقدر الإمكان:"إذا كنت ترغب في إنشاء نوع تريد أن تتصرف بقدر الإمكان كما المدمج في أنواع هنا هي أدوات..."

أعتقد أنها مشابهة جدا .صافي قرار لجعل كل بدائية البنية ، بما في ذلك القيم المنطقية, الصحيحه, الخ., وجميع البنيات مستمدة من وجوه.هذا القرار وحده يضع .صافي أبعد من جاوة إلى عند العمل مع الأوليات ، لا يهم كم الملاكمة/علبته الخارقة جافا إضافة إلى مواصفاتها.

هل كنت حقا في حاجة إليها في C++?

هذا السؤال لك للإجابة.لا Bjarne Stroustrup.لا عشب ساتر.ليس مهما عضو في C++ القياسية اللجنة.هذا هو السبب لديك الخيار في C++, و لن تحد من المفيد تدوين المدمج في أنواع وحده.

إذا لك في حاجة إليها ، ثم هو إضافة نرحب بها.إذا لك لا, حسنا...لا تستخدم.لن يكلفك الأمر شيئا.

مرحبا بكم في C++ لغة فيها ميزات اختيارية.

المتضخمة???تظهر المجمعات!!!

هناك فرق بين المتضخمة المعقدة (يقصد التورية).

مثل ما هو مبين من قبل في نيلز ما قدرات جديدة هل المعرفة من قبل المستخدم حرفية إضافة إلى C++?, ، أن تكون قادرة على كتابة معقدة عدد هو واحد من اثنين من الميزات المضافة "في الآونة الأخيرة" إلى C و C++:

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

الآن كل C99 "مزدوجة معقدة" نوع C++ "std::مجمع" النوع قادرة على ضرب وأضاف تطرح ، وما إلى ذلك ، باستخدام عامل الحمولة الزائدة.

ولكن في C99, هم فقط تم إضافة نوع آخر المدمج في نوع المدمج في مشغل إثقال الدعم.و أضافوا آخر المدمج في حرفية الميزة.

في C++, هم فقط استخدام الميزات الموجودة في اللغة ، ورأى أن الحرفي الميزة الطبيعية لتطور اللغة ، ومن ثم إضافته.

في ج ، إذا كنت بحاجة إلى نفس منهج تعزيز نوع آخر, كنت خارجا من الحظ حتى الضغط على إضافة الكم وظائف موجة (أو 3D نقطة أو ما الأساسية نوع تستخدمه في مجال العمل الخاص بك) إلى C القياسية المدمج في نوع ينجح.

في C++11, يمكنك أن تفعل ذلك بنفسك:

Point p = 25_x + 13_y + 3_z ; // 3D point

هل هي منتفخة ؟ لا, الحاجة هناك ، كما أظهرت كيف أن كل من C و C++ المجمعات بحاجة إلى وسيلة لتمثيل الحرفية المعقدة القيم.

هل هو خطأ مصممة ؟ لا, إنه تم تصميم كل C++, مع التمدد في الاعتبار.

هو تدوين الأغراض فقط ؟ لا, كما يمكن إضافة نوع السلامة إلى التعليمات البرمجية الخاصة بك.

على سبيل المثال, دعونا نتخيل CSS الموجهة كود:

css::Font::Size p0 = 12_pt ;       // Ok
css::Font::Size p1 = 50_percent ;  // Ok
css::Font::Size p2 = 15_px ;       // Ok
css::Font::Size p3 = 10_em ;       // Ok
css::Font::Size p4 = 15 ;         // ERROR : Won't compile !

ومن ثم من السهل جدا لتطبيق قوي كتابة على احالة من القيم.

هل هو خطير ؟

سؤال جيد.أن هذه الوظائف تكون namespaced?إذا كان الجواب نعم, ثم الفوز بالجائزة الكبرى!

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

لذا, مثل كل C++ الميزة ، هل حقا في حاجة إليها ؟ هذا هو السؤال الذي يجب الإجابة قبل استخدامه في C++.إذا كنت لا, لن يكلفك أي شيء.ولكن إذا كنت حقا في حاجة إليها, على الأقل, اللغة لن نخذلكم.

تاريخ سبيل المثال ؟

الخطأ, كما يبدو لي, هو أن يتم خلط المشغلين:

1974/01/06AD
    ^  ^  ^

هذا لا يمكن تجنبها ، لأن / كونه مشغل المترجم أن يفسر ذلك.و AFAIK, بل هو شيء جيد.

أن تجد حلا لمشكلتك ، أود أن أكتب الحرفي في بعض طريقة أخرى.على سبيل المثال:

"1974-01-06"_AD ;   // ISO-like notation
"06/01/1974"_AD ;   // french-date-like notation
"jan 06 1974"_AD ;  // US-date-like notation
19740106_AD ;       // integer-date-like notation

شخصيا, وأود أن اختيار صحيح و ISO التواريخ ، ولكن ذلك يعتمد على الاحتياجات الخاصة بك.وهو بيت القصيد من السماح للمستخدم تحديد الخاصة الحرفي الأسماء.

انها لطيفة جدا على مدونة رياضية.من رأيي أنا يمكن أن نرى استخدام العوامل التالية:

درجة جامعية.الذي يجعل الكتابة المطلقة زوايا أكثر بديهية.

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

فإنه يمكن أيضا أن تستخدم لمختلف نقطة ثابتة تمثيل (والتي لا تزال تستخدم في مجال DSP و الرسومات).

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

هذه تبدو لطيفة أمثلة على كيفية استخدامها.أنها تساعد على جعل الثوابت في التعليمات البرمجية أكثر قابلية للقراءة.انها أداة أخرى لجعل رمز غير قابل للقراءة فضلا, ولكن لدينا بالفعل الكثير من أدوات تعاطي ذلك لا يضر كثيرا.

UDLs هي namespaced (و يمكن استيرادها باستخدام الإعلانات/توجيهات, ولكن لا يمكنك بوضوح مساحة الحرفي مثل 3.14std::i) مما يعني أن هناك (أمل) لن تكون طن من الاشتباكات.

حقيقة أنها يمكن أن تكون في الواقع قالب (و constexpr كنت) يعني أنه يمكنك أن تفعل بعض الاشياء قوية جدا مع UDLs.Bigint الكتاب سوف تكون سعيدة حقا لأنها أخيرا تعسفا كبيرة الثوابت ، وتحسب في وقت الترجمة (عبر constexpr أو قوالب).

أنا فقط حزين أننا لن نرى زوجين مفيدة حرفية في مستوى (على ما يبدو) ، s بالنسبة std::string و i للوحدة وهمية.

كمية من وقت الترميز التي سيتم حفظها من قبل UDLs هو في الواقع ان القراءة سوف يكون إلى حد كبير زيادة المزيد والمزيد من العمليات الحسابية يمكن أن تحول إلى تجميع الوقت لسرعة التنفيذ.

اسمحوا لي أن أضيف قليلا من السياق.عملنا يحددها المستخدم حرفية هو تشتد الحاجة إليها.نحن نعمل على MDE (نموذج يحركها الهندسة).أردنا أن تحدد نماذج metamodels في C++.ونحن في الواقع تنفذ تعيين من Ecore إلى C++ (EMF4CPP).

المشكلة تأتي عندما تكون قادرة على تحديد عناصر النموذج كما دروس في C++.ونحن نتخذ نهج تحويل metamodel (Ecore) إلى قوالب مع الحجج.حجج القالب هي الخصائص الهيكلية من أنواع الطبقات.على سبيل المثال فئة مع اثنين الباحث سمات سيكون شيئا مثل:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

Hoever ، اتضح أن كل عنصر في نموذج أو metamodel ، عادة ما يكون له اسم.نود أن نكتب:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

ولكن, C++ أو C++0x لا تسمح بذلك ، كسلاسل محظورة كما الحجج إلى قوالب.يمكنك كتابة اسم فحم من فحم, ولكن هذا هو admitedly الفوضى.مع حسن المعرفة من قبل المستخدم حرفية ، يمكننا أن نكتب شيئا من هذا القبيل.أقول نحن نستخدم "_n" لتحديد نموذج عنصر أسماء (لا يستخدم نفس الجملة, فقط لجعل فكرة):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

أخيرا ، وبعد تلك التعاريف كقوالب تساعدنا كثيرا في تصميم الخوارزميات عبور عناصر النموذج, نموذج التحولات ، إلخ.التي هي فعالة حقا ، لأنه نوع المعلومات, تحديد, التحولات, الخ.يتم تحديدها من قبل برنامج التحويل البرمجي في وقت الترجمة.

Bjarne Stroustrup يتحدث عن UDL في هذا C++11 الحديث, في القسم الأول على نوع-واجهات غنية حوالي 20 دقيقة مارك.

له الحجة الأساسية بالنسبة UDLs يأخذ شكل القياس المنطقي:

  1. "تافهة" أنواع ، أي المدمج في أنواع بدائية, يمكن التقاط فقط تافهة نوع من الأخطاء.واجهات مع أغنى أنواع تسمح نوع النظام للقبض على المزيد من أنواع الأخطاء.

  2. أنواع من نوع الأخطاء التي غنية كتابة التعليمات البرمجية يمكن أن قبض على رمز حقيقي.(انه يعطي مثالا على المريخ المناخ المداري ، والتي سيئ السمعة فشلت بسبب الأبعاد خطأ في مهمة ثابت).

  3. في رمز حقيقي, وحدات نادرا ما تستخدم.الناس لا تستخدم لهم ، لأن تكبد وقت حساب أو الذاكرة النفقات العامة إلى خلق أنواع غنية مكلف جدا ، باستخدام القائمة من قبل C++ قالب وحدة الشفرة notationally القبيح أن لا أحد يستخدمه.(تجريبيا ، لا أحد يستخدم ذلك ، على الرغم من المكتبات قد حول لفترة من الزمان).

  4. ولذلك ، من أجل الحصول على المهندسين أن استخدام وحدات في رمز حقيقي, نحن في حاجة إلى جهاز (1) يتحمل أي وقت التشغيل والنفقات العامة (2) هو notationally مقبول.

دعم الترجمة الوقت البعد التحقق هو المبرر الوحيد المطلوب.

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

انظر على سبيل المثال PhysUnits-CT-Cpp11, صغيرة C++11, C++14 رأس فقط مكتبة وقت التحويل البرمجي الأبعاد تحليل وحدة/كمية التلاعب و التحويل.أبسط من دفعة.الوحدات, هل الدعم رمز الوحدة حرفية مثل m, g, s ، البادئات المترية مثل m, k, M, فقط يعتمد على معيار مكتبة C, SI فقط ، لا يتجزأ من القوى الأبعاد.

همم...أنا لم أفكر في هذه الميزة حتى الآن.العينة كانت مدروسة و هو بالتأكيد مثيرة للاهتمام.C++ هي قوية جدا كما هو عليه الآن ، ولكن للأسف الجملة المستخدمة في قطعة من التعليمات البرمجية كنت اقرأ في بعض الأحيان معقدة للغاية.القراءة ، إن لم يكن كلها, ثم على الأقل بكثير.و مثل هذه الميزة ستكون موجهة للحصول على مزيد من القراءة.إذا كنت تأخذ المثال الأخير

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

...أتساءل كيف يمكنك التعبير عن ذلك اليوم.سيكون لديك كجم و LB الدرجة وكنت مقارنة ضمنية الكائنات:

assert(KG(1.0f) == LB(2.2f));

و التي من شأنها أن تفعل كذلك.مع الأنواع التي لها أسماء أطول أو الأنواع التي لا يوجد لديك أمل وجود مثل لطيفة منشئ بلا كتابة محول, قد يكون بالإضافة إلى لطيفة على ذبابة الضمني إنشاء الكائن و التهيئة.من ناحية أخرى, يمكنك إنشاء و تهيئة الكائنات باستخدام الأساليب أيضا.

ولكن أنا أتفق مع نيلس على الرياضيات.C و C++ وظائف علم المثلثات على سبيل المثال تتطلب إدخال بالراديان.أعتقد في درجة حتى فترة قصيرة جدا التحويل الضمني مثل نيلز نشر هو لطيف جدا.

في نهاية المطاف سيكون النحوية السكر ومع ذلك ، ولكن سيكون لها تأثير طفيف على القراءة.وأنه من المحتمل أن يكون من الأسهل لكتابة بعض العبارات أيضا (sin(180.0 درجة) هو أسهل في الكتابة من الخطيئة(deg(180.0)).ومن ثم سيكون هناك الناس الذين يسيئون استخدام هذا المفهوم.ولكن بعد ذلك, اللغة المسيئة الناس أن استخدام تقييدية للغاية اللغات بدلا من شيء معبرة بقدر C++.

آه ، يا آخر يقول أساسا لا شيء ما عدا:انها تسير على ما يرام ، أثر لن تكون كبيرة جدا.دعونا لا نقلق.:-)

لم يسبق لي حاجة أو أرادت هذه الميزة (ولكن هذا يمكن أن يكون Blub تأثير).ركبتي رد فعل أحمق هو عرجاء ، و من المرجح أن أناشد نفس الناس الذين يعتقدون أنه من الرائع أن الزائد المشغل+ أي العملية التي يمكن بعد أن يفسر إضافة.

C++ عادة ما تكون صارمة جدا حول بناء الجملة المستخدم - منع المعالج ليس هناك الكثير يمكنك استخدامها لتحديد مخصص الجملة/النحوي.E. g.يمكننا الزائد الموجودة operatos, ولكن لا نستطيع أن نحدد جديدة - المنظمة البحرية الدولية هذا كثيرا في تناغم مع روح C++.

أنا لا أمانع بعض الطرق للحصول على مزيد من تخصيص شفرة المصدر - ولكن النقطة المختارة يبدو معزولة جدا بالنسبة لي ، والذي يحيرني أكثر.

حتى الاستخدام المقصود قد تجعل من الصعب جدا قراءة التعليمات البرمجية المصدر:وهو حرف واحد قد يكون واسع المدى الآثار الجانبية التي لا يمكن تحديدها من السياق.مع التماثل إلى u, l, f, معظم المطورين سوف تختار واحدة الحروف.

هذا ويمكن أيضا تحديد النطاق في مشكلة باستخدام الحروف المفردة في مساحة العالمي ربما تعتبر ممارسة سيئة ، والأدوات التي من المفترض خلط المكتبات أسهل (مساحات وصفي معرفات) ربما هزيمة الغرض منه.

أرى بعض الجدارة في تركيبة مع "السيارات", كذلك في تركيبة مع وحدة المكتبة مثل دفعة وحدة, ولكن ليس بما فيه الكفاية لتستحق هذا adition.

أتساءل ما هي أفكار ذكية نأتي مع.

أنا استخدم مستخدم حرفية الثنائية سلاسل مثل هذا:

 "asd\0\0\0\1"_b

باستخدام std::string(str, n) منشئ بحيث \0 لا قطع السلسلة في نصف.(هل المشروع الكثير من العمل مع تنسيقات الملفات المختلفة.)

هذا كان مفيدا أيضا عندما سقطت std::string لصالح المجمع std::vector.

خط الضوضاء في هذا الشيء ضخم.كما انها الرهيبة القراءة.

اسمحوا لي أن أعرف, هل هم السبب في أن جملة جديدة بالإضافة إلى ذلك مع أي نوع من الأمثلة ؟ على سبيل المثال, هل لديهم بعض البرامج التي تستخدم بالفعل C++0x?

بالنسبة لي هذا الجزء:

auto val = 3.14_i

لا يبرر هذا الجزء:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

حتى إذا كنت تستخدم i-بناء الجملة في 1000 خطوط أخرى كذلك.إذا كنت أكتب ، ربما أكتب 10000 خطوط من أي شيء آخر على ذلك أيضا.وخصوصا عندما كنت سوف ربما لا يزال يكتب في الغالب في كل مكان هذا:

std::complex<double> val = 3.14i

'السيارات' -الكلمة قد يكون لها ما يبررها على الرغم من, فقط ربما.ولكن دعونا نلقي فقط C++, لأن ذلك أفضل من C++0x في هذا الجانب.

std::complex<double> val = std::complex(0, 3.14);

انها مثل..بهذه البساطة.حتى ظننت أن كل الأمراض المنقولة جنسيا و مدبب بين قوسين هي مجرد سخيفة إذا كنت تريد استخدامه في كل مكان.لا تبدأ التخمين ما الجملة يوجد في C++0x لتحويل std::مجمع تحت المعقدة.

complex = std::complex<double>;

هذا ربما شيء بسيط, ولكن لا أعتقد أن الأمر بسيط في C++0x.

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

ربما ؟ >:)

على أية حال،:كتابة 3.14 أنا بدلا من std::مجمع(0, 3.14);لا يوفر لك الكثير من الوقت في العام إلا في بعض السوبر الحالات الخاصة.

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