ก่อนอื่นหลายคนอาจจะมีคำถามที่ว่าแล้ว C++11 คืออะไร? ต่างจาก C++ ธรรมดายังไง? คำตอบก็คือว่า C++11 เป็นมาตรฐานของ C++ ตัวใหม่ครับ ซึ่งเป็นมาตรฐานที่ออกมาในปี 2011 คือหมายถึงเถียงกันเสร็จเรียบร้อยแล้วว่าจะให้มีหรือไม่มีอะไรบ้าง อย่างไร ซึ่งถ้าย้อนกลับไปตอนแรกเหมือนมาตรฐานนี้จะใช้ชื่อแบบลำลองว่า C++0x นั่นไม่แน่ใจว่าจะเสร็จในปีไหนแต่คงมี 200x นี่แหละ ซึ่งพอเอาเข้าจริงก็ลากยากมาถึงปี 2011 เลยทีเดียว ถึงขนาดมีคนเล่นมุกว่า 0x ที่ว่าคือ 0B (เลขฐาน 16) นั่นเอง (ฮา)
ตอนที่กำลังเขียนอยู่นี่ก็ต้นปี 2016 เข้าไปแล้ว จริงๆแล้วมันก็มีมาตรฐานใหม่ออกมาเพิ่มอีกแหละครับคือ C++14 กับ C++17 แต่ว่าคราวนี้เรามาดูกันก่อนว่าไอ้เจ้า C++11 นี่มันมีอะไรต่างจากเดิมมั่ง มันเจ๋งขึ้นจริงๆ รึเปล่า?
ก่อนที่จะใช้ C++11 เนี่ยก็จำเป็นที่จะต้องใช้ตัวคอมไพเลอร์ที่ Implement ฟีเจอร์ต่างๆของ C++11 ได้ทั้งหมดแล้ว ซึ่งหาไม่ยากเลยครับ g++, clang ตัวใหม่ หรือจะเป็นคอมไพเลอร์ของ Intel และ Microsoft ก็ใช้ได้หมดแล้ว สามารถดูรายละเอียดว่ามีตัวไหนใช้อะไรได้บ้างที่ C++ compiler support เลยครับ
ทีนี้เรามาดูกันเลยดีกว่าว่ามันมีอะไรที่ทำให้ชีวิตดีขึ้นบ้าง
1. auto
มันคือการที่เราสามารถกำหนดชนิดตัวแปรเป็น auto แล้วให้คอมไพเลอร์ไปหาเองว่ามันควรจะเป็นชนิดอะไรครับ พูดแล้วเห็นภาพยาก ลองดูตัวอย่างดีกว่า
auto x = 20; auto y = 4.5; auto z = 'A';
จากตัวอย่างบรรทัดแรกคือ เราไม่ต้องประกาศตัวแปร x เป็นชนิด int ให้เห็นแต่ว่า ใช้ auto ไปเลย คอมไพลเอร์จะเดาชนิดให้เองจาก ชนิดของด้านขวามือ ซึ่งก็คือ int เพราะมันเห็นว่า 20 เป็น int ครับ ตัวแปร y ก็จะหลายเป็น double และ z ก็จะเป็น char แน่นอนว่าคีย์เวิร์ด auto ก็มีข้อจำกัดอยู่ เช่นถ้าเราไม่มีการกำหนดค่ามันก็จะเดาชนิดให้ไม่ออกครับ เช่นประกาศ auto s; แบบนี้ไม่ได้แน่นอนเพราะไม่มีชนิดในมันอ้างอิง (ยังไงตัวแปรใน C++ ก็ต้องกำหนดชนิดข้อมูล เหมือนเดิมครับ เพียงแต่ว่าเราแค่ปล่อยให้มันไปหาเองได้ว่าควรจะเป็นชนิดอะไร)
ตัวอย่างเมื่อกี้เป็นแค่ตัวอย่างให้เห็นภาพครับ ยังไม่ใช่ประโยชน์ที่แท้จริงเท่าไหร่ เพราะมันดูไม่ช่วยอะไรเท่าไหร่ มันจะช่วยได้มากในกรณีต่อไปนี้ครับ
vector<int> v; /* คำสั่งอื่นๆ */ vector<int>::iterator it = v.begin(); vector< map<int,string> > mapv; /* คำสั่งอื่นๆ */ map<int,string> m = mapv[5];
การประกาศชนิดของ iterator หรือว่าชนิดที่เป็น template ทั้งหลายจะสูญเสียพลังงานในการพิมพ์มาก เราก็จะเปลี่ยนเป็นใช้ auto แทน ซึ่งเสียเวลาพิมพ์น้อยกว่า โค้ดก็อ่านง่ายสบายตากว่าเดิมเยอะ เช่นแบบนี้ครับ
vector<int> v; /* คำสั่งอื่นๆ */ auto = v.begin(); vector< map<int,string> > mapv; /* คำสั่งอื่นๆ */ auto m = mapv[5];
2. แก้ไขการ parse <>
อันนี้นึกไม่ออกว่าจะเรียกมันว่าอะไร แต่ C++ มีข้อจำกัดที่โคตรจะน่ารำคาญอย่างนึงคือเวลาเขียนชนิดที่เป็น template ซ้อนกันจะต้องเว้นวรรคระหว่าง > อย่างน้อย 1 ทีครับ เช่น ถ้าเราอยากจะสร้าง matrix ขึ้นมาอันนึงเราอาจจะใช้ vector<vector<int>> mat; แต่ว่าเขียนแบบนี้ถือว่าผิด เพราะคอมไพเลอร์จะเข้าใจว่าไอ้ >> ติดกันนี่คือโอเปอเรเตอร์ >> ซึ่งถ้าจะให้ถูกจะต้องเขียนว่า vector< vector<int> > mat; เพื่อกันมันสับสน
แต่ว่า C++11 ไม่โง่แล้วครับ เขียนไปได้เลย vector<vector<int>> มันไม่มึนแล้ว ชีวิตสดใสขึ้นเยอะเลย
3. ลูป for แบบ range
ภาษาสมัยใหม่มันก็มีกันหมดแล้วล่ะ ก็เลยเป็นอีก 1 ฟีเจอร์ที่ขาดไปไม่ได้ จากที่แต่ก่อนเราอาจจะต้องวนลูปตัวอาร์เรย์หรือ containner ต่างๆ แล้วค่อย get ค่ามาจากการใช้ index แบบนี้
vector<int> v; for (int i = 0; i < v.size(); ++i) { int x = v[i]; /* เอาค่า x ไปทำอะไรซักอย่าง */ } for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) { int x = *it; /* เอาค่า x ไปทำอะไรซักอย่าง */ }
พอเห็นแล้วก็จะรู้สึกว่ายาวเหลือเกิน จริงๆมันมันเขียนแบบสั้นๆ แต่ได้ความหมายเหมือนเดิม ด้วยการใช้ลูปแบบ range ตามนี้เลยครับ
for (int x : v) { /* เอาค่า x ไปทำอะไรซักอย่าง */ }
หรือจะเอา auto มาใช้ก็ได้ เกิดถ้าชนิดของ element ใน container มันยาว
vector< map<int,string> > mapv; for (auto m : mapv) { /* เอาค่า m ไปทำอะไรซักอย่าง */ }
4. List initialization
อีกตัวที่ผมรู้สึกว่ามันควรจะมีมานานมากแล้ว เพราะเขียน C++ แล้วอึดอัดเหลือเกินกับปัญหาการกำหนดค่าให้ container ลองดูตัวอย่างต่อไปนี้นะครับ
int a[5] = {1,2,3,4,5}; /* ทำได้ */ vector<int> v = {1,2,3,4,5}; /* ทำไม่ได้ */ /* ถ้าจะได้ต้องเอาอาร์เรย์มากำหนดให้มันอีกต่อนึงแบบนี้ */ vector<int> v(a,a+5);
ปัญหาก็ประมาณนี้แหละครับ คือมันกำหนดค่าเริ่มต้นยากมาก ดังนั้นใน C++11 ก็เลยยอมให้ทำได้เรียบร้อยแล้ว
vector<int> v = {1,2,3,4,5}; /* ทำได้ใน C++11 */ vector<int> v2{1,2,3,4,5}; /* แบบนี้ก็ได้ (C++11) */ /* กำหนดค่าไปแล้วอยากเปลี่ยนกลางอากาศก็ทำได้ สุดยอด!! */ v = {100,200,300};
ผมก็สุดยอดไปงั้นแหละครับ พวกภาษาสมัยใหม่มันเขียนได้อยู่แล้ว แต่ C++ มันทำไม่ได้มาอย่างนาน พอได้แล้วมันก็ต้องดีใจบ้าง (ฮา) นอกจากนี้ยังกำหนดค่าแบบ nested ได้ด้วยนะ อย่างเช่นพวก map
map<int,string> m = { {1, "one"}, {2, "two"}, {3, "three"} };
5. Lambda functions
Labmda functions ถ้ามองผ่านก็คือฟังก์ชันที่ไม่มีชื่ออันนึง แต่จริงๆแล้วมันมีอะไรมากกว่าฟังก์ชันธรรมดาครับ มันมีส่วนที่เรียกว่า capture ซึ่งสามารถนำ context ภายนอก เช่นตัวแปรต่างๆเข้าไปใช้ในฟังก์ชันที่สร้างขึ้นได้ ซึ่งขอไม่ลงลึกนะครับ ไปดูตัวอย่างการใช้แบบง่ายๆกันก่อนดีกว่า
bool moreThan10(int x) { return x>10; } vector<int> v = {6,5,10,13,25,1,25,23,13,5,8,90}; int c = count_if(v.begin(), v.end(), moreThan10);
ฟังก์ชัน count_if ใน <algorithm> ใช้สำหรับนับว่าใน container มีข้อมูลกี่ตัวที่ตรงกับเงื่อนไขที่กำหนดให้ ซึ่งเราจะต้องส่งฟังก์ชันสำหรับกำหนดเงื่อนไขเข้าไปตัวนึง ทีนี้ Lambdas จะมีประโยชน์ตรงนี้แหละครับ คือมันสามารถสร้างฟังก์ชันที่ไม่มีชื่อแล้วส่งเข้าไปตรงนั้นได้เลย
vector<int> v = {6,5,10,13,25,1,25,23,13,5,8,90}; int c = count_if(v.begin(), v.end(), [](int x){return x>10;});
ซึ่งมันจะเหมาะมากกับฟังก์ชันแบบที่ใช้ครั้งเดียวแล้วก็เลิกลากันไป ไม่ต้องมาประกาศให้กวนใจกันอีกครับ
ตรง [] คือส่วนของ capture ที่บอกว่าจะเอาตัวแปรอะไรเข้าไปใช้ในฟังก์ชันบ้าง ซึ่งในที่นี้เป็นว่างเพราะเราไม่เอาอะไรเข้าไปเลย (int x) อันนี้ก็พารามิเตอร์ของฟังก์ชันเฉยๆ
สมมติว่าอยากให้เช็คว่ามีกี่ตัวบ้างที่มีค่ามากกว่า q
vector<int> v = {6,5,10,13,25,1,25,23,13,5,8,90}; int q = 8; int c = count_if(v.begin(), v.end(), [q](int x){return x>q;});
ตรง capture ก็ใส่ตัวแปร q เข้าไป เพื่อบอกว่าให้เอาตัวแปร q ไปใช้ในฟังก์ชันด้วย ซึ่งความหมายของคำสั่งดังกล่าวคือนับจำนวนของข้อมูลใน vector v ที่มากกว่า q (ซึ่งมีค่าเท่ากับ 8)
จาก concept ของ lambdas ทำให้ประกาศตัวแปรฟังก์ชันใน C++11 ได้ง่ายขึ้นมากครับ (เมื่อก่อนมันก็ทำได้แต่ลำบาก) เช่นจากตัวอย่างแรกสุด เราก็จะได้โปรแกรมใหม่หน้าตาแบบนี้ ใช้ชนิดเป็น auto ไปได้เลยครับ
auto moreThan10 = [](int x){return x>10;}; int c = count_if(v.begin(), v.end(), moreThan10);
อื่นๆ
- เพิ่มฟังก์ชันที่เกี่ยวกับ Regular Expression ใน <regex>
- เพิ่มฟังก์ชันที่เกี่ยวกับการสุ่มใน <random>
- Raw string literals ที่เขียน string literal แบบหลายบรรทัดได้ง่ายขึ้น
- คีย์เวิร์ด override ที่เอาไว้บอกว่าฟังก์ชันนี้เป็นการ overriding และคีย์เวิร์ด final ที่ทำให้ฟังก์ชันไม่สามารถ overriding ต่อได้ (หรือกำหนดให้ class ไม่สามารถ inherite ต่อได้)
- มีฟังก์ชันใหม่ๆอีกมากมาย, smart pointer, enum แบบใหม่, tuple, การจัดการ thread, atomic operator และอื่นๆอีกมากมายครับ