ความสามารถใหม่อะไรที่ตัวอักษรที่ผู้ใช้กำหนดเองเพิ่มไว้ใน C ++


139

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(เป็นธรรมดาint) และหลังจากนั้น06_AD(จะไม่พูดอะไรของเดือนสิงหาคมและกันยายนจะต้องเขียนโดยไม่มี0เหตุผลแปด) สิ่งนี้สามารถแก้ไขได้โดยให้มีไวยากรณ์1974-1/6_ADเพื่อให้คำสั่งการประเมินผลตัวดำเนินการทำงานได้

ดังนั้นคำถามของฉันคืออะไรคุณคิดว่าคุณสมบัตินี้จะพิสูจน์ตัวเองหรือไม่ ตัวอักษรอื่น ๆ ที่คุณต้องการกำหนดว่าจะทำให้โค้ด C ++ ของคุณอ่านง่ายขึ้น?


อัปเดตไวยากรณ์เพื่อให้พอดีกับร่างสุดท้ายในเดือนมิถุนายน 2011


8
ฉันจะลงคะแนนให้ปิดนี้ ชื่อค่อนข้างชัดเจนอย่างชัดเจน
ลูกสุนัข

76
@DeadMG หากคุณมีปัญหาด้วยชื่อที่คุณสามารถแก้ไขได้ มันค่อนข้างตลกที่พยายามปิดคำถามอายุ 3 ปีที่มีผู้อัปโหลด 11 คนและ 8 รายการโปรด (ไม่ได้กล่าวถึงว่ามารยาทบนไซต์นี้มีการเปลี่ยนแปลงในช่วง 3 ปีที่ผ่านมา)
Motti

5
ฉันคิดว่าคุณมีข้อผิดพลาดในตัวอย่างของคุณ: ไม่สามารถใช้ในการแยกstring operator "" _s(const char*s);" "hello"_s"นี่คือสตริงตัวอักษรและจะค้นหาโอเปอเรเตอร์ที่มีsize_tพารามิเตอร์เพิ่มเติม ฉันถูกไหม?
towi

1
สิ่งหนึ่งที่ผมเคยสงสัยเกี่ยวกับการเป็นว่ามันจะให้ความรู้สึกที่จะเขียน "C แบบพกพา" ใน C ++, เปลี่ยนประเภทเช่นuint16_tมีพฤติกรรมคือการดำเนินการขึ้นอยู่กับประเภทที่คล้ายกันuwrap16และunum16มีพฤติกรรมจะดำเนินการอิสระดังกล่าวที่ได้รับuwrap16 w=1; unum16 n=1;การแสดงออกw-2และn-2จะให้ผลผลิต(uwrap16)65535และ(int)-1ตามลำดับ [ uint16_tจะให้ผลลัพธ์แรกในระบบที่intมี 16 บิตและที่สองในระบบที่intมีขนาดใหญ่กว่า] ปัญหาที่ใหญ่ที่สุดที่ฉันเห็นคือการจัดการตัวอักษรตัวเลข
supercat

1
ความสามารถในการทำให้ตัวอักษรตัวเลขทำงานร่วมกันอย่างราบรื่นกับประเภทตัวเลขพฤติกรรมที่กำหนดอื่น ๆ ดูเหมือนว่าควรอนุญาตให้ใช้ประเภทดังกล่าวเพื่อสร้างภาษาที่รหัสที่ต้องการดำเนินการตามการดำเนินการสามารถทำได้โดยไม่ต้องพึ่งพาการนำไปใช้งาน - พฤติกรรมที่กำหนดไว้ มีบางสถานที่ที่ IDB จะยังคงหลีกเลี่ยงไม่ได้เพราะสิ่งต่าง ๆ เช่นตัวชี้ความแตกต่างและsizeofประเภทจำนวนเต็มขึ้นอยู่กับการใช้งานคืน แต่สถานการณ์ก็ยังสามารถทำให้ดีขึ้นกว่าเดิม คุณคิดอย่างไรกับแนวคิดนั้น
supercat

คำตอบ:


71

นี่คือกรณีที่มีข้อได้เปรียบในการใช้ตัวอักษรที่ผู้ใช้กำหนดเองแทนการเรียกคอนสตรัคเตอร์:

#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");
}

ข้อดีคือข้อยกเว้นเวลาทำงานถูกแปลงเป็นข้อผิดพลาดในการคอมไพล์ คุณไม่สามารถเพิ่มการยืนยันแบบคงที่ให้กับ ctor ของชุดบิตรับสตริง (อย่างน้อยก็ไม่ต้องไม่มีอาร์กิวเมนต์เท็มเพลตสตริง)


7
คุณสามารถทำสิ่งเดียวกันได้โดยให้ std :: bitset เป็นตัวสร้าง constexpr ที่เหมาะสม
Nicol Bolas

1
@NicolBolas คุณพูดถูก ฉันแปลกใจจริงๆที่ไม่มีในนั้น บางทีเราควรเสนอหนึ่งหรือสองสำหรับปี 2014 หากยังไม่สายเกินไป
emsr

192

ตั้งแต่แรกเห็นมันดูเหมือนว่าจะเป็นน้ำตาล syntactic ง่าย ๆ

แต่เมื่อมองลึกลงไปเราจะเห็นว่ามันเป็นเรื่องของน้ำตาลมากกว่าวากยสัมพันธ์เพราะมันขยายตัวเลือกผู้ใช้ C ++ เพื่อสร้างประเภทที่ผู้ใช้กำหนดเองซึ่งมีลักษณะเหมือนกับชนิดในตัวที่แตกต่างกัน ในเรื่องนี้ "โบนัส" เล็กน้อยนี้เป็นสิ่งที่น่าสนใจมากใน C ++ 11 นอกเหนือจาก C ++

เราต้องการมันจริงๆใน C ++ หรือไม่?

ผมเห็นการใช้งานไม่กี่คนในรหัสฉันเขียนไว้ในปีที่ผ่านมา แต่เพียงเพราะผมไม่ได้ใช้มันใน C ++ ไม่ได้หมายความว่ามันไม่ได้เป็นที่น่าสนใจสำหรับอีก c ++ นักพัฒนา

เราใช้ใน C ++ (และใน C, ฉันเดา), ตัวอักษรที่กำหนดโดยคอมไพเลอร์, เพื่อพิมพ์ตัวเลขจำนวนเต็มเป็นจำนวนเต็มสั้นหรือยาว, จำนวนจริงเป็น float หรือ double (หรือยาวเป็นสองเท่า), และอักขระสตริงเป็นตัวอักษรธรรมดาหรือกว้าง .

ใน C ++ เรามีความเป็นไปได้ในการสร้างประเภทของเราเอง (เช่นคลาส) โดยไม่มีค่าใช้จ่าย (inline) เรามีความเป็นไปได้ที่จะเพิ่มโอเปอเรเตอร์ให้กับประเภทของพวกเขาเพื่อให้พวกมันมีพฤติกรรมคล้ายกับชนิดในตัวซึ่งช่วยให้นักพัฒนา C ++ สามารถใช้เมทริกซ์และจำนวนเชิงซ้อนตามธรรมชาติอย่างที่ควรจะเป็น เราสามารถเพิ่มผู้ดำเนินการส่ง (ซึ่งโดยปกติจะเป็นความคิดที่ไม่ดี แต่บางครั้งมันเป็นเพียงทางออกที่ถูกต้อง)

เรายังคงพลาดสิ่งหนึ่งที่มีประเภทผู้ใช้เป็นแบบในตัว: ตัวอักษรที่ผู้ใช้กำหนด

ดังนั้นฉันคิดว่ามันเป็นวิวัฒนาการตามธรรมชาติของภาษา แต่ต้องทำให้สมบูรณ์ที่สุดเท่าที่จะเป็นไปได้: " ถ้าคุณต้องการสร้างประเภทและคุณต้องการให้มันมีพฤติกรรมที่เป็นไปได้มากที่สุดเท่าที่มีในตัวนี่คือเครื่องมือ .. "

ฉันเดาว่ามันคล้ายกับการตัดสินใจของ. NET ที่จะทำให้โครงสร้างดั้งเดิมทุกตัวรวมถึงบูลีน, จำนวนเต็ม, ฯลฯ และมีโครงสร้างทั้งหมดมาจากวัตถุ การตัดสินใจนี้เพียงอย่างเดียวทำให้. NET อยู่ไกลเกินเอื้อมของ Java เมื่อทำงานกับ primitives ไม่ว่า Java / Boxing box ที่ไม่มีแฮ็กจะเพิ่มจำนวนมากเท่าใด

คุณต้องการมันจริงๆใน C ++ หรือไม่?

คำถามนี้สำหรับคุณที่จะตอบ ไม่ใช่ Bjarne Stroustrup ไม่ใช่สมุนไพรซัทเทอร์ ไม่ใช่สมาชิกของคณะกรรมการมาตรฐาน C ++ นี่คือเหตุผลที่คุณมีตัวเลือกใน C ++และพวกเขาจะไม่ จำกัด สัญลักษณ์ที่มีประโยชน์สำหรับประเภทในตัวเพียงอย่างเดียว

หากคุณต้องการมันก็เป็นการต้อนรับที่เพิ่มเข้ามา ถ้าคุณทำไม่ดี ... อย่าใช้มัน มันจะเสียค่าใช้จ่ายอะไร

ยินดีต้อนรับสู่ C ++ ภาษาที่คุณสมบัติเป็นตัวเลือก

ป่อง??? แสดงคอมเพล็กซ์ของคุณให้ฉันดู !!!

มีความแตกต่างระหว่างป่องและซับซ้อน (ปุนตั้งใจ)

เช่นเดียวกับที่แสดงโดย Niels ที่ตัวอักษรที่ผู้ใช้กำหนดเองเพิ่มความสามารถใหม่ให้กับ 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 "double complex" type และ C ++ "std :: complex" สามารถที่จะทำการคูณบวกบวกลบ ฯลฯ โดยใช้ตัวดำเนินการโอเวอร์โหลดได้

แต่ใน C99 พวกเขาเพิ่งเพิ่มประเภทอื่นเป็นชนิดในตัวและรองรับการใช้งานมากเกินไปในตัว และพวกเขาก็เพิ่มฟีเจอร์ตามตัวอักษรอีกแบบหนึ่ง

ใน C ++ พวกเขาใช้คุณลักษณะภาษาที่มีอยู่แล้วเห็นว่าคุณสมบัติตามตัวอักษรเป็นวิวัฒนาการตามธรรมชาติของภาษาและเพิ่มเข้ามา

ใน C หากคุณต้องการการปรับปรุงสัญกรณ์เดียวกันสำหรับประเภทอื่นคุณไม่มีโชคจนกว่าคุณจะวิ่งเต้นเพื่อเพิ่มฟังก์ชั่นคลื่นควอนตัมของคุณ (หรือจุด 3 มิติหรือประเภทพื้นฐานที่คุณใช้ในสาขาการทำงาน) ไปยัง มาตรฐาน 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 !

จากนั้นง่ายมากในการบังคับใช้การพิมพ์ที่มีประสิทธิภาพให้กับการกำหนดค่า

เป็นอันตรายหรือไม่?

คำถามที่ดี. สามารถตั้งชื่อฟังก์ชั่นเหล่านี้ได้หรือไม่? ถ้าใช่แจ็คพอต!

อย่างไรก็ตามทุกอย่างคุณสามารถฆ่าตัวเองถ้าเครื่องมือที่ถูกนำมาใช้อย่างไม่ถูกต้อง C นั้นทรงพลังและคุณสามารถยิงหัวของคุณถ้าคุณใช้ปืน C ในทางที่ผิด 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 แต่ขึ้นอยู่กับความต้องการของคุณ ซึ่งเป็นจุดรวมของการให้ผู้ใช้กำหนดชื่อตามตัวอักษรของมันเอง


1
ขอขอบคุณฉันและบุคคลอื่นที่มีบุคลิกของฉันถูกค้นพบแล้ว อย่างจริงจังยิ่งขึ้นฉันเขียนเพียงอย่างเดียว แต่บางทีฉันอาจใช้การแสดงออกของภาษาพื้นเมืองของฉันและพวกเขาไม่สามารถแปลเป็นภาษาอังกฤษได้ดี
paercebal

สำหรับ "ส่วนต่าง ๆ " ที่แสดงโดยชื่อของพวกเขาขอโทษฉันเดาว่าการจัดระเบียบค่อนข้างโพสต์ค่อนข้างยาว สำหรับข้อความตัวหนามันเป็นบทสรุปของย่อหน้าพวกเขาคนที่ต้องการเฉพาะข้อมูลที่ไม่มีเหตุผลสามารถ จำกัด การอ่านของพวกเขาในชื่อและข้อความตัวหนา
paercebal

3
+1 คำอธิบายที่ดีจริงๆ เรากำลังรอการดำเนินการนี้ มันสำคัญมากสำหรับเรา เราทำงานกับ MDE (Model-Driven Engineering) และพบสิ่งนี้เป็นสิ่งจำเป็น ฉันกำลังเพิ่มคำตอบด้านล่างเพื่ออธิบายกรณีของเรา
Diego Sevilla

9
@TGV: you can write 1+2i, but you still can't write a+bi, so there's absolutely no pointแม้แต่การเพิกเฉยa+biตัวอย่างของคุณก็ไร้สาระความจริงที่คุณเห็นว่าเป็น "ความถี่ต่ำ" ไม่ได้หมายความว่าทุกคนทำ . . การมองภาพรวมจุดสำคัญคือเพื่อให้แน่ใจว่าวัตถุที่ผู้ใช้กำหนดสามารถเป็นพลเมืองชั้นหนึ่งของภาษาได้มากเท่าที่เป็นไปได้ ดังนั้นถ้าคุณสามารถเขียน1.5fและ1000ULทำไมคุณไม่สามารถเขียน25iหรือแม้กระทั่ง100101b? ตรงกันข้ามกับ C และ Java ประเภทผู้ใช้จะไม่ถูกพิจารณาว่าเป็นพลเมืองชั้นสองของภาษาใน C ++
paercebal

3
@Anton: Most of data still comes from IOมีโค้ดฮาร์โค้ดจำนวนมาก ดูที่ booleans ทั้งหมดจำนวนเต็มทั้งหมดทุกคู่ที่มาในรหัสเพราะมันเป็นความสะดวกมากขึ้นในการเขียนx = 2 * y ;แทนการx = Two * yที่Twoเป็นอย่างยิ่งพิมพ์อย่างต่อเนื่อง ตัวอักษรที่กำหนดโดยผู้ใช้ทำให้เราสามารถพิมพ์และเขียน: x = 2_speed * y ;และให้คอมไพเลอร์ตรวจสอบว่าการคำนวณนั้นสมเหตุสมผล . . ทุกอย่างมาเกี่ยวกับการพิมพ์ที่แข็งแกร่ง . . บางทีคุณจะไม่ใช้มัน แต่ฉันแน่ใจว่าจะทันทีที่ฉันจะสามารถใช้คอมไพเลอร์เปิดใช้งาน C ++ 11 ในที่ทำงาน
paercebal

36

มันดีมากสำหรับรหัสทางคณิตศาสตร์ จากความคิดของฉันฉันเห็นการใช้งานสำหรับตัวดำเนินการต่อไปนี้:

องศาสำหรับองศา นั่นทำให้การเขียนมุมสัมบูรณ์ใช้งานง่ายยิ่งขึ้น

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); 
}

ตัวอย่างเหล่านี้มีลักษณะที่ดีว่าจะใช้อย่างไร มันช่วยให้ค่าคงที่ในโค้ดอ่านง่ายขึ้น เป็นอีกเครื่องมือหนึ่งที่ทำให้โค้ดไม่สามารถอ่านได้เช่นกัน แต่เรามีเครื่องมือที่ใช้ในทางที่ผิดแล้วและอีกอย่างหนึ่งนั้นไม่ได้ทำร้ายอะไรมาก


1
"แต่เรามีเครื่องมือที่ใช้ในทางที่ผิดแล้วซึ่งไม่ได้ทำร้ายอะไรมากมาย " ว้าวฉันหวังว่านั่นไม่ใช่ปรัชญาที่อยู่เบื้องหลังสิ่งที่ c ++ [x] 1234567890 กำลังเกิดน้ำท่วมเมื่อไม่นานมานี้ ลองนึกภาพว่าต้องเรียนรู้ห้องสมุดที่ใช้สิ่งเหล่านี้ทั้งหมดรวมถึงรูปแบบไฟล์สิบรูปแบบสำหรับการกำหนดค่าและเครื่องมือสองอย่างสำหรับโค้ดก่อนและหลังการประมวลผลของคุณ ...
masterxilo

@masterxilo แน่นอนว่าในความเป็นจริงตัวอย่างของคุณนั้นไร้สาระ: UDL นั้นไม่ยากที่จะเรียนรู้มากกว่าฟังก์ชั่นเพราะเพียงแค่ซินแท็กซ์น้ำตาลสำหรับพวกเขา - และนอกจากนี้ lib ที่ดีเท่านั้นที่ใช้คุณสมบัติตามที่จำเป็นในการปรับปรุง UX ทุกวิถีทาง หากมีคนใช้คุณลักษณะมากเกินไปในการสร้างรหัสที่อ่านไม่ได้ (สมมติว่านั่นเป็นสิ่งที่หลีกเลี่ยงได้ในสายงานของพวกเขา ... ) มันจะไม่ทำให้คุณสมบัตินั้นผิดพลาดเพียงการใช้งาน นอกจากนี้บุคคลที่อ่านไม่ได้คือขนมปังและเนยของอีกคน มันเป็นความคิดเห็นทั้งหมด - และตัวเลือก หากคุณไม่ชอบพวกเขาไม่ต้องกังวล! คุณไม่ต้องใช้มัน คนอื่น ๆก็สามารถทำได้
underscore_d

17

UDL เป็นเนมสเปซ (และสามารถนำเข้าโดยใช้การประกาศ / คำสั่ง แต่คุณไม่สามารถกำหนดเนมสเปซตามตัวอักษรอย่างชัดเจน3.14std::i) ซึ่งหมายความว่ามี (หวังว่า) จะไม่ขัดแย้งกันมากมาย

ความจริงที่ว่าพวกเขาสามารถทำเท็มเพลตได้ (และใช้งานจริง) หมายความว่าคุณสามารถทำบางสิ่งที่ทรงพลังกับ UDL ได้ ผู้เขียน Bigint จะมีความสุขมากเพราะในที่สุดพวกเขาสามารถมีค่าคงที่ขนาดใหญ่โดยพลการคำนวณ ณ เวลารวบรวม (ผ่านทาง constexpr หรือเทมเพลต)

ฉันแค่เสียใจที่เราจะไม่เห็นตัวอักษรที่มีประโยชน์สองสามตัวในมาตรฐาน (จากรูปลักษณ์ของมัน) เช่นsสำหรับstd::stringและiหน่วยจินตภาพ

จำนวนเวลาการเข้ารหัสที่จะถูกบันทึกโดย UDL นั้นไม่สูงมากนัก แต่ความสามารถในการอ่านจะเพิ่มขึ้นอย่างมากมายและการคำนวณที่มากขึ้นสามารถเปลี่ยนเป็นการรวบรวมเวลาเพื่อการดำเนินการที่รวดเร็วยิ่งขึ้น


ขอบคุณที่ล้างจุดเกี่ยวกับเนมสเปซ ... ฉันสงสัยว่า
นาธานรีด

12

ขอผมเพิ่มบริบทหน่อย สำหรับงานของเราจำเป็นต้องใช้ตัวอักษรที่กำหนดโดยผู้ใช้มาก เราทำงานกับ MDE (วิศวกรรมแบบขับเคลื่อน) เราต้องการที่จะกำหนดรูปแบบและ metamodels ใน C ++ เราใช้การทำแผนที่จาก Ecore ไปยัง C ++ ( EMF4CPP )

ปัญหาเกิดขึ้นเมื่อสามารถกำหนดองค์ประกอบของโมเดลเป็นคลาสใน C ++ เรากำลังใช้แนวทางในการแปลง Metamodel (Ecore) เป็นเทมเพลตที่มีข้อโต้แย้ง อาร์กิวเมนต์ของเทมเพลตคือลักษณะโครงสร้างของชนิดและคลาส ตัวอย่างเช่นคลาสที่มีสองแอ็ตทริบิวต์ int จะเป็นดังนี้:

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

Hoever ปรากฎว่าทุกองค์ประกอบในรูปแบบหรือ metamodel มักจะมีชื่อ เราต้องการเขียน:

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

BUT, C ++ หรือ C ++ 0x ไม่อนุญาตสิ่งนี้เนื่องจากข้อห้ามในการใช้เป็นอาร์กิวเมนต์สำหรับแม่แบบ คุณสามารถเขียนชื่อถ่านด้วยถ่าน แต่มันเป็นระเบียบเรียบร้อย ด้วยตัวอักษรที่ผู้ใช้กำหนดเองเราสามารถเขียนสิ่งที่คล้ายกัน สมมติว่าเราใช้ "_n" เพื่อระบุชื่อองค์ประกอบของแบบจำลอง (ฉันไม่ได้ใช้ไวยากรณ์ที่ถูกต้องเพียงเพื่อให้ความคิด):

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

สุดท้ายการมีคำจำกัดความเหล่านั้นเป็นแม่แบบช่วยให้เราออกแบบอัลกอริทึมสำหรับการสำรวจองค์ประกอบโมเดลการแปลงแบบจำลอง ฯลฯ ที่มีประสิทธิภาพมากเพราะข้อมูลประเภทการระบุการเปลี่ยนแปลง ฯลฯ ถูกกำหนดโดยคอมไพเลอร์ในเวลารวบรวม


4
ผมชอบมาก ๆby the compiler at compile timeส่วนหนึ่ง ... :-)
paercebal

12

Bjarne Stroustrup พูดถึง UDL ในการพูดคุย C ++ 11นี้ในส่วนแรกของอินเทอร์เฟซที่มีประเภทหลากหลายประมาณ 20 นาที

อาร์กิวเมนต์พื้นฐานของเขาสำหรับ UDL ใช้รูปแบบของการอ้างเหตุผล:

  1. ประเภท "เบ็ดเตล็ด" คือประเภทดั้งเดิมในตัวสามารถรับข้อผิดพลาดประเภทเล็กน้อยได้ การเชื่อมต่อที่มีประเภทยิ่งขึ้นทำให้ระบบประเภทสามารถตรวจจับข้อผิดพลาดได้มากขึ้น

  2. ชนิดของข้อผิดพลาดประเภทที่โค้ดที่พิมพ์สมบูรณ์สามารถตรวจจับได้มีผลกับโค้ดจริง (เขายกตัวอย่างของยานอวกาศ Climate Mars ซึ่งน่าอับอายล้มเหลวเนื่องจากข้อผิดพลาดมิติในค่าคงที่ที่สำคัญ)

  3. ในรหัสจริงหน่วยจะไม่ค่อยได้ใช้ ผู้คนไม่ได้ใช้พวกเขาเพราะการคำนวณแบบรันไทม์หรือค่าใช้จ่ายหน่วยความจำในการสร้างประเภทที่หลากหลายนั้นมีค่าใช้จ่ายสูงเกินไปและการใช้รหัสหน่วย C + templated ที่มีอยู่แล้วนั้นเป็นสิ่งที่น่าเกลียดอย่างมาก (สังเกตุไม่มีใครใช้เลยแม้ว่าห้องสมุดจะมีมานานกว่าทศวรรษ)

  4. ดังนั้นเพื่อให้วิศวกรใช้หน่วยในรหัสจริงเราจำเป็นต้องมีอุปกรณ์ที่ (1) ไม่มีค่าใช้จ่ายในการดำเนินการและ (2) เป็นที่ยอมรับอย่างไม่สมควร


9

การสนับสนุนการตรวจสอบมิติเวลาแบบคอมไพล์เป็นเพียงข้ออ้างที่จำเป็นเท่านั้น

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

assert(energy == 4_J); 

ดูตัวอย่างPhysUnits-CT-Cpp11 , ไลบรารีส่วนหัว C ++ 11, C ++ 14 ขนาดเล็กเท่านั้นสำหรับการวิเคราะห์มิติเวลาคอมไพล์และการจัดการและการแปลงหน่วย / ปริมาณ เรียบง่ายกว่าBoost.Unitsรองรับสัญลักษณ์ตัวอักษรหน่วยเช่น m, g, s, คำนำหน้าตัวชี้วัดเช่น m, k, M ขึ้นอยู่กับไลบรารี C ++ มาตรฐาน, SI-only, พลังรวมของมิติ


หรือดูหน่วยเป็นเวลารวบรวมส่วนหัวเท่านั้นการวิเคราะห์มิติและห้องสมุดแปลงหน่วยที่สร้างขึ้นบน C ++ 14 ที่มีการอ้างอิงโดยไม่ได้Nic Holthaus
Martin Moene

6

อืม ... ฉันยังไม่ได้นึกถึงคุณสมบัตินี้เลย ตัวอย่างของคุณคิดออกมาดีและน่าสนใจอย่างแน่นอน C ++ นั้นทรงพลังมากเหมือนตอนนี้ แต่น่าเสียดายที่ไวยากรณ์ที่ใช้ในส่วนของโค้ดที่คุณอ่านนั้นซับซ้อนเกินไป ความสามารถในการอ่านคือถ้าไม่ใช่ทั้งหมดอย่างน้อยก็มาก และคุณสมบัติดังกล่าวจะเหมาะสำหรับการอ่านเพิ่มเติม ถ้าฉันนำตัวอย่างสุดท้ายของคุณ

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

... ฉันสงสัยว่าวันนี้คุณจะแสดงออกอย่างไร คุณมีคลาส KG และ LB และคุณจะเปรียบเทียบวัตถุโดยนัย:

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

และนั่นก็จะทำเช่นกัน ด้วยประเภทที่มีชื่อหรือประเภทที่ยาวกว่าซึ่งคุณไม่มีความหวังที่จะมีตัวสร้างที่ดีสำหรับ sans ที่เขียนอะแดปเตอร์มันอาจเป็นการเพิ่มที่ดีสำหรับการสร้างและกำหนดค่าเริ่มต้นของวัตถุแบบ on-the-fly ในทางกลับกันคุณสามารถสร้างและเริ่มต้นวัตถุด้วยวิธีการได้เช่นกัน

แต่ฉันเห็นด้วยกับนิลส์ในวิชาคณิตศาสตร์ ฟังก์ชันตรีโกณมิติ C และ C ++ ต้องการตัวอย่างอินพุตเป็นเรเดียน ฉันคิดว่าเป็นองศาดังนั้นการแปลงโดยนัยสั้น ๆ อย่างนิลส์ที่โพสต์นั้นดีมาก

ท้ายที่สุดมันจะเป็นน้ำตาลซินแทคติค แต่มันจะมีผลเพียงเล็กน้อยต่อการอ่านได้ และอาจจะง่ายกว่าในการเขียนสำนวนบางอย่างเช่นกัน (sin (180.0deg) ง่ายต่อการเขียนมากกว่า sin (deg (180.0)) จากนั้นจะมีคนที่ใช้แนวความคิดในทางที่ผิด แต่แล้วคนที่ใช้ภาษาที่ไม่เหมาะสมควรใช้ ภาษาที่มีข้อ จำกัด มากกว่าภาษาที่มีความหมายเหมือนภาษาซีพลัสพลัส

อาโพสต์ของฉันบอกว่าไม่มีอะไรยกเว้น: มันจะไม่เป็นไรผลกระทบจะไม่ใหญ่เกินไป ไม่ต้องห่วงหรอก :-)


5
วงเล็บของคุณไม่สมดุล! ขออภัย OCD ของฉันก็เกลียดฉันเช่นกัน
X-Istence

3

ฉันไม่เคยต้องการหรือต้องการคุณสมบัตินี้ (แต่นี่อาจเป็นเอฟเฟกต์Blub ) ปฏิกิริยากระตุกหัวเข่าของฉันคือมันอ่อนแอและมีแนวโน้มที่จะดึงดูดคนคนเดียวกันที่คิดว่ามันเจ๋งที่จะโอเวอร์โหลดโอเปอเรเตอร์ + สำหรับการดำเนินการใด ๆ ที่อาจตีความได้ว่าเป็นการเพิ่มจากระยะไกล


ฉันยืนยัน: บทความที่น่าสนใจมาก
paercebal

2

C ++ มักจะเข้มงวดมากเกี่ยวกับไวยากรณ์ที่ใช้ - ยกเว้นตัวประมวลผลล่วงหน้ามีไม่มากที่คุณสามารถใช้เพื่อกำหนดไวยากรณ์ / ไวยากรณ์ที่กำหนดเอง เช่นเราสามารถโอเวอร์โหลดโอเปอเรเตอร์ที่มีอยู่ได้ แต่เราไม่สามารถกำหนดโอเปอร์เรเตอร์ใหม่ได้ - IMO นี่สอดคล้องกับจิตวิญญาณของ C ++ มาก

ฉันไม่สนใจวิธีที่จะปรับแต่งซอร์สโค้ดให้มากขึ้น - แต่จุดที่เลือกนั้นดูเหมือนจะโดดเดี่ยวสำหรับฉันซึ่งทำให้ฉันสับสนมากที่สุด

แม้การใช้ที่ตั้งใจไว้อาจทำให้อ่านซอร์สโค้ดได้ยากขึ้น: จดหมายฉบับเดียวอาจมีผลข้างเคียงมากมายที่ไม่สามารถระบุได้จากบริบท ด้วยความสมมาตรกับ u, l และ f นักพัฒนาส่วนใหญ่จะเลือกตัวอักษรเดียว

สิ่งนี้อาจเปลี่ยนการกำหนดขอบเขตเป็นปัญหาโดยใช้ตัวอักษรตัวเดียวในเนมสเปซส่วนกลางอาจถูกพิจารณาว่าเป็นการปฏิบัติที่ไม่ดีและเครื่องมือที่ควรผสมห้องสมุดได้ง่ายขึ้น (เนมสเปซและตัวระบุที่สื่อความหมาย) อาจเอาชนะวัตถุประสงค์

ฉันเห็นข้อดีบางอย่างร่วมกับ "อัตโนมัติ" รวมกับหน่วยไลบรารีเช่นหน่วยเพิ่มแต่ไม่เพียงพอที่จะทำบุญ adition นี้

อย่างไรก็ตามฉันสงสัยว่าความคิดที่ฉลาดของเราเกิดขึ้นได้อย่างไร


1
using single letters in global namespace will probably be considered bad practiceแต่นั่นไม่มีความเกี่ยวข้อง: (A) UDL ต้องถูกกำหนดที่ขอบเขตเนมสเปซ (ไม่ใช่โลก) ... น่าจะเป็นเพราะ (B) พวกเขาจะต้องประกอบด้วยเครื่องหมายขีดล่างแล้ว> = 1 ตัวอักษรไม่ใช่แค่ตัวอักษรและตัวระบุดังกล่าวใน โกลบอล NS ถูกสงวนไว้สำหรับการนำไปใช้งาน นั่นคืออย่างน้อย 2 คะแนนต่อความคิดที่ว่า UDL สร้างความสับสนโดยกำเนิด สำหรับขอบเขตของเนมสเปซจะลดคุณสมบัติของยูทิลิตี้นั่นคือสาเหตุที่ stdlib ประกาศว่าพวกเขาinline namespaceสามารถนำเข้าขายส่งได้ถ้าต้องการ
underscore_d

2

ฉันใช้ตัวอักษรผู้ใช้สำหรับสตริงไบนารีเช่นนี้

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

ใช้ตัวstd::string(str, n)สร้างเพื่อที่\0จะไม่ตัดสายครึ่ง (โครงการทำงานได้หลายอย่างกับรูปแบบไฟล์ต่างๆ)

นี้จะเป็นประโยชน์เมื่อฉันทิ้งในความโปรดปรานของเสื้อคลุมสำหรับstd::stringstd::vector


-5

เสียงของสายในสิ่งนั้นใหญ่มาก นอกจากนี้ยังเป็นที่น่ากลัวในการอ่าน

แจ้งให้เราทราบพวกเขาให้เหตุผลว่าการเพิ่มไวยากรณ์ใหม่ด้วยตัวอย่างชนิดใด ๆ ตัวอย่างเช่นพวกเขามีสองโปรแกรมที่ใช้ C ++ 0x อยู่แล้วหรือไม่

สำหรับฉันตอนนี้:

auto val = 3.14_i

ไม่ปรับส่วนนี้:

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

ไม่แม้ว่าคุณจะใช้ i-syntax ในอีก 1,000 บรรทัดเช่นกัน ถ้าคุณเขียนคุณอาจเขียน 10,000 บรรทัดของสิ่งอื่นตามนั้นเช่นกัน โดยเฉพาะอย่างยิ่งเมื่อคุณจะยังคงเขียนส่วนใหญ่ทุกที่นี้:

std::complex<double> val = 3.14i

'อัตโนมัติ' - คำหลักอาจได้รับการพิสูจน์ แต่ลองเอาแค่ C ++ เพราะมันดีกว่า C ++ 0x ในแง่นี้

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

มันเหมือน .. ที่ง่าย แม้จะคิดว่า std และ pointy ทั้งหมดเป็นเพียงแค่ง่อยถ้าคุณใช้มันทุกที่ ฉันไม่เริ่มเดาว่ามีไวยากรณ์อะไรใน C ++ 0x สำหรับการเปลี่ยน std :: complex ภายใต้ complex

complex = std::complex<double>;

อาจเป็นสิ่งที่ตรงไปตรงมา แต่ฉันไม่เชื่อว่ามันง่ายใน C ++ 0x

typedef std::complex<double> complex;

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

บางที? > :)

อย่างไรก็ตามประเด็นคือ: เขียน 3.14i แทน std :: complex (0, 3.14); ไม่ได้ช่วยให้คุณประหยัดเวลาโดยรวมยกเว้นในกรณีพิเศษบางอย่าง


10
@Cheery: สำหรับคุณ "auto val = 3.14i" ไม่ปรับรหัสที่เขียนเพื่อรองรับ ฉันสามารถตอบได้ว่าสำหรับฉัน "printf ("% i ", 25)" ไม่ปรับรหัสที่เขียนสำหรับ printf คุณเห็นรูปแบบหรือไม่
paercebal

5
@Cheery: "เสียงเส้นในสิ่งนั้นใหญ่มาก" ไม่มันไม่ใช่ ... "และมันก็น่ากลัวที่จะอ่าน" อาร์กิวเมนต์อัตนัยของคุณน่าสนใจ แต่คุณควรดูโอเปอเรเตอร์ที่มีการโหลดมากเกินไปโดยทั่วไปเพื่อดูโค้ดสำหรับคุณลักษณะนี้อยู่ไกลจากที่น่าประหลาดใจ / ตกตะลึง ... สำหรับนักพัฒนา C ++
paercebal

3
อัตโนมัติจะช่วยให้อ่านง่าย ลองใช้ interators ใน for for loop: for (auto it = vec.begin (); it! = vec.end (); ++ มัน) ... ฉันรู้เกี่ยวกับ for_each แต่ไม่ชอบการสร้าง functor เพื่อใช้งาน .
KitsuneYMG

1
@kts: ด้วย C ++ 1x เราจะมีแลมบ์ดาและช่วงสำหรับ
Joe D

3
หากบรรทัด C ++ ของคุณดีกว่า C ++ 0x แสดงว่าบรรทัดของฉันดีกว่า เขียนแค่: std::complex<double> val(0, 3.14);.
Ben Voigt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.