รหัส C ++ สามารถใช้ได้ทั้งใน C ++ 03 และ C ++ 11 แต่ทำสิ่งที่แตกต่างกันหรือไม่


298

เป็นไปได้หรือไม่ที่รหัส C ++ จะสอดคล้องกับทั้งมาตรฐานC ++ 03และมาตรฐานC ++ 11แต่ทำสิ่งที่แตกต่างกันไปขึ้นอยู่กับมาตรฐานที่กำลังรวบรวม?


26
ฉันค่อนข้างมั่นใจว่าautoจะส่งผลให้สถานการณ์เช่นนี้
OMGtechy

8
ใช่. ตัวอย่างหนึ่งคือ>>เมื่อใช้ในเทมเพลต คุณสามารถสร้างสถานการณ์ที่สามารถรวบรวมมาตรฐานทั้งสองได้ อีกอย่างหนึ่งที่ฉันแน่ใจว่าจะหาได้ง่ายคือการเริ่มต้น
chris

5
นี่เป็นบทความที่ดีเกี่ยวกับ >> สถานการณ์: gustedt.wordpress.com/2013/12/15/…
chris

6
@OMGtechy: ฉันไม่คิดว่า autoจะทำให้เกิดสิ่งนี้ ด้วยความหมายเก่าการautoประกาศต้องใช้ชื่อประเภท ด้วยความหมายใหม่ไม่อนุญาตให้พิมพ์ชื่อ
Keith Thompson

2
มันเป็นอย่างไรเปิดปลาย? คุณเองชี้ให้เห็นอีกคำถามหนึ่งที่คำตอบของคำถามนี้คือ "ใช่นี่คือตัวอย่างของวิธีการ" มีคำตอบที่ชัดเจนมากสำหรับคำถามตามที่คุณชี้ให้เห็น
jalf

คำตอบ:


283

คำตอบคือใช่แน่นอน ในด้านบวกมี:

  • รหัสที่ก่อนหน้านี้คัดลอกวัตถุโดยนัยตอนนี้จะย้ายโดยปริยายเมื่อเป็นไปได้

ด้านลบมีตัวอย่างหลายรายการในภาคผนวก C ของมาตรฐาน แม้ว่าจะมีจำนวนลบมากไปกว่าบวก แต่แต่ละข้อก็มีโอกาสเกิดขึ้นได้น้อยกว่ามาก

สตริงตัวอักษร

#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"

และ

#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal

พิมพ์การแปลงค่าเป็น 0

ใน C ++ 11 เฉพาะตัวอักษรเท่านั้นที่ยังคงเป็นตัวชี้โมฆะค่าคงที่:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
    f(0*N); // Calls #2; used to call #1
}

ผลลัพธ์ที่ปัดเศษขึ้นหลังจากการหารจำนวนเต็มและโมดูโล

ใน C ++ 03 คอมไพเลอร์ได้รับอนุญาตให้ปัดไปทาง 0 หรือไปทางอนันต์ลบ ใน C ++ 11 จะต้องปัดเศษเป็น 0

int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0

ช่องว่างระหว่างวงเล็บปิดเทมเพลตที่ซ้อนกัน >> vs>>

ภายในความเชี่ยวชาญหรือการสร้างอินสแตนซ์>>อาจตีความได้ว่าเป็นการเปลี่ยนที่ถูกต้องใน C ++ 03 นี่เป็นแนวโน้มที่จะทำลายรหัสที่มีอยู่แม้ว่า: (จากhttp://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )

template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);

void total(void) {
    // fon<fun<9> >(1) >> 2 in both standards
    unsigned int A = fon< fun< 9 > >(1) >>(2);
    // fon<fun<4> >(2) in C++03
    // Compile time error in C++11
    unsigned int B = fon< fun< 9 >>(1) > >(2);
}

โอเปอเรเตอร์newอาจมีข้อยกเว้นอื่นนอกเหนือจากนี้std::bad_alloc

struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
    foo *f = new foo();
} catch (std::bad_alloc &) {
    // c++03 code
} catch (std::exception &) {
    // c++11 code
}

destructors ที่ผู้ใช้ประกาศมี ตัวอย่างข้อกำหนดข้อยกเว้นโดยนัยจากการเปลี่ยนแปลงการแยกใดที่นำมาใช้ใน C ++ 11

struct A {
    ~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try { 
    A a; 
} catch(...) { 
    // C++03 will catch the exception
} 

size() ของคอนเทนเนอร์จำเป็นต้องเรียกใช้ใน O (1)

std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03

std::ios_base::failureไม่ได้มาโดยตรงจากstd::exceptionอีกต่อไป

ในขณะที่คลาสพื้นฐานโดยตรงเป็นของใหม่std::runtime_errorไม่ใช่ ดังนั้น:

try {
    std::cin >> variable; // exceptions enabled, and error here
} catch(std::runtime_error &) {
    std::cerr << "C++11\n";
} catch(std::ios_base::failure &) {
    std::cerr << "Pre-C++11\n";
}

11
ดี +1 อีกคนหนึ่งคือผู้ใช้ destructor ประกาศในขณะนี้คือโดยปริยายnoexecpt(true)ดังนั้นthrowในตอนนี้จะ destructor std::terminateโทร แต่ฉันหวังว่าทุกคนที่เขียนรหัสดังกล่าวจะมีความสุขกับเรื่องนี้!
typ1232

4
แต่มาตรฐาน :: system_error ตัวเองเป็น (อ้อม) ที่ได้มาจากมาตรฐาน :: ข้อยกเว้นเพื่อให้ยังคงจับcatch (std::exception &) std::ios_base::failure
user2665887

@ user2665887 คุณพูดถูก มันยังคงมีอิทธิพลต่อพฤติกรรมของโปรแกรม แต่ฉันไม่สามารถนึกถึงตัวอย่างที่น้อยที่สุดได้ในขณะนี้
ตัวอย่าง

4
ฉันสับสนมากอย่างที่คุณพูดถึงoperator newมันถูกต้อง (ตอนนี้มันสามารถโยนได้std::bad_array_new_length) แต่ตัวอย่างของคุณไม่แสดงให้เห็นเลย รหัสที่คุณแสดงนั้นเหมือนกันใน C ++ 03 และ C ++ 11 AFAIK
Mooing Duck

2
ด้านพลิกของรายการ :: ขนาดกำลัง O (1) คือประกบกันตอนนี้ O (n)
Tony Delroy

55

ฉันนำคุณไปยังบทความนี้และการติดตามผลซึ่งมีตัวอย่างที่ดีว่า>>จะเปลี่ยนความหมายจาก C ++ 03 เป็น C ++ 11 ได้อย่างไรขณะที่ยังคงรวบรวมทั้งสองอย่าง

bool const one = true;
int const two = 2;
int const three = 3;

template<int> struct fun {
    typedef int two;
};

template<class T> struct fon {
    static int const three = ::three;
    static bool const one = ::one;
};

int main(void) {
    fon< fun< 1 >>::three >::two >::one; // valid for both  
}

ส่วนสำคัญคือบรรทัดmainซึ่งเป็นนิพจน์

ใน C ++ 03:

1 >> ::three = 0
=> fon< fun< 0 >::two >::one;

fun< 0 >::two = int
=> fon< int >::one

fon< int >::one = true
=> true

ใน C ++ 11

fun< 1 > is a type argument to fon
fon< fun<1> >::three = 3
=> 3 > ::two > ::one

::two is 2 and ::one is 1
=> 3 > 2 > 1
=> (3 > 2) > 1
=> true > 1
=> 1 > 1
=> false

ขอแสดงความยินดีสองผลลัพธ์ที่แตกต่างกันสำหรับนิพจน์เดียวกัน จริงอยู่ที่ C ++ 03 มีรูปแบบคำเตือนดังขึ้นเมื่อฉันทดสอบ


มันเป็นเรื่องแปลกที่มันไม่จำเป็นต้องtypenameสำหรับ::twoใน C ++ 03 รุ่น
ซาฮีร์

3
ดีมากนำไปต้มเพื่อประเมินtrueหรือfalseมาตรฐานที่แตกต่างกัน บางทีเราอาจใช้เป็นแบบทดสอบคุณลักษณะ </joke>
cmaster - คืนสถานะโมนิก้า

@ ซาฮีร์มันไม่ได้เป็นเพียงแค่ประเภท
chris

ตัวเลือก cmdline ที่เหมาะสมเตือนเกี่ยวกับสิ่งนี้ ( warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]) แต่ก็ยังเป็นตัวอย่างที่ดีว่าตัว::ดำเนินการที่คลุมเครือเปลี่ยนความหมายอย่างไร (อ้างอิงจากขอบเขตทั่วโลกหรือการอ้างอิงที่ยืนอยู่ตรงหน้า)
ตัวอย่าง

@ ตัวอย่าง, น่าแปลกที่ GCC ให้คำเตือนนั้น แต่เสียงดังกังวานไม่ได้
chris

39

ใช่มีจำนวนการเปลี่ยนแปลงที่จะทำให้รหัสเดียวกันส่งผลให้เกิดพฤติกรรมที่แตกต่างกันระหว่าง C ++ 03 และ C ++ 11 ความแตกต่างของกฎการจัดลำดับทำให้เกิดการเปลี่ยนแปลงที่น่าสนใจรวมถึงพฤติกรรมบางอย่างที่ไม่ได้กำหนดไว้ก่อนหน้านี้ซึ่งได้กำหนดไว้อย่างดี

1. การกลายพันธุ์ของตัวแปรเดียวกันหลายรายการภายในรายการ initializer

กรณีมุมที่น่าสนใจมากอย่างหนึ่งจะกลายพันธุ์ของตัวแปรเดียวกันหลายรายการภายในรายการ initializer ตัวอย่างเช่น:

int main()
{
    int count = 0 ;
    int arrInt[2] = { count++, count++ } ;

    return 0 ;
}

ทั้งใน C ++ 03 และ C ++ 11 นี้ถูกกำหนดไว้อย่างดี แต่คำสั่งของการประเมินผลใน C ++ 03 ไม่ได้ระบุแต่ในC ++ 11 พวกเขาได้รับการประเมินในลำดับที่ปรากฏ ดังนั้นถ้าเราคอมไพล์โดยใช้clangในโหมด C ++ 03 มันให้คำเตือนต่อไปนี้ ( ดูสด ):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced]

    int arrInt[2] = { count++, count++ } ;

                           ^        ~~

แต่ไม่ได้ให้คำเตือนใน C ++ 11 ( ดูแบบสด )

2. กฎการเรียงลำดับใหม่ทำให้ i = ++ i + 1; กำหนดไว้อย่างดีใน C ++ 11

กฎการเรียงลำดับใหม่ที่ใช้หลังจาก C ++ 03 หมายความว่า:

int i = 0 ;
i = ++ i + 1;

ไม่มีพฤติกรรมที่ไม่ได้กำหนดใน C ++ 11 อีกต่อไปซึ่งจะกล่าวถึงในรายงานข้อบกพร่อง 637 กฎการเรียงลำดับและตัวอย่างไม่เห็นด้วย

3. กฎการเรียงลำดับใหม่ยังทำให้ ++++ i; กำหนดไว้อย่างดีใน C ++ 11

กฎการเรียงลำดับใหม่ที่ใช้หลังจาก C ++ 03 หมายความว่า:

int i = 0 ;
++++i ;

ไม่มีพฤติกรรมที่ไม่ได้กำหนดใน C ++ 11 อีกต่อไป

4. กะซ้ายซ้ายที่มีความหมายมากกว่าเล็กน้อย

ร่างต่อมาของภาษา C ++ 11 รวมN3485ซึ่งผมลิงค์ข้างล่างนี้คงไม่ได้กำหนดพฤติกรรมของการขยับ 1 บิตเข้าหรือที่ผ่านมาบิตเครื่องหมาย นอกจากนี้ยังจะครอบคลุมในรายงานข้อบกพร่อง 1457 Howard Hinnant แสดงความคิดเห็นเกี่ยวกับความสำคัญของการเปลี่ยนแปลงนี้ในเธรดบนการเลื่อนซ้าย (<<) พฤติกรรมที่ไม่ได้กำหนดจำนวนเต็มลบใน C ++ 11 หรือไม่ .

5. ฟังก์ชัน constexpr สามารถใช้เป็นนิพจน์ค่าคงที่เวลาแบบคอมไพล์ใน C ++ 11

C ++ 11 แนะนำฟังก์ชันconstexprซึ่ง:

ตัวระบุ constexpr ประกาศว่าเป็นไปได้ที่จะประเมินค่าของฟังก์ชันหรือตัวแปร ณ เวลารวบรวม ตัวแปรและฟังก์ชั่นดังกล่าวนั้นสามารถใช้งานได้เฉพาะเมื่อได้รับอนุญาตให้มีการแสดงออกอย่างต่อเนื่องคงที่ของเวลา

ในขณะที่ C ++ 03 ไม่ได้มีconstexprคุณสมบัติที่เราไม่จำเป็นต้องใช้อย่างชัดเจนconstexprคำหลักตั้งแต่ห้องสมุดมาตรฐานให้หลายหน้าที่ใน C ++ 11 constexpr ยกตัวอย่างเช่นมาตรฐาน :: numeric_limits :: นาที ซึ่งสามารถนำไปสู่พฤติกรรมที่แตกต่างเช่น:

#include <limits>

int main()
{
    int x[std::numeric_limits<unsigned int>::min()+2] ;
}

การใช้clangใน C ++ 03 สิ่งนี้จะทำให้xเป็นอาร์เรย์ความยาวผันแปรซึ่งเป็นส่วนขยายและจะสร้างคำเตือนต่อไปนี้:

warning: variable length arrays are a C99 feature [-Wvla-extension]
    int x[std::numeric_limits<unsigned int>::min()+2] ;
         ^

ในขณะที่ C ++ 11 std::numeric_limits<unsigned int>::min()+2เป็นนิพจน์ค่าคงที่เวลาคอมไพล์และไม่ต้องการส่วนขยาย VLA

6. ใน C ++ 11 ข้อยกเว้นข้อยกเว้น noexcept ถูกสร้างขึ้นโดยปริยายสำหรับ destructors ของคุณ

เนื่องจากใน C ++ 11 destructor ที่ผู้ใช้กำหนดเองมีnoexcept(true)ข้อกำหนดโดยนัยตามที่อธิบายไว้ในตัวทำลายล้าง noexceptนั่นหมายความว่าโปรแกรมต่อไปนี้:

#include <iostream>
#include <stdexcept>

struct S
{
  ~S() { throw std::runtime_error(""); } // bad, but acceptable
};

int main()
{
  try { S s; }
  catch (...) {
    std::cerr << "exception occurred";
  } 
 std::cout << "success";
}

ใน C ++ 11 จะโทรstd::terminateแต่จะทำงานได้สำเร็จใน C ++ 03

7. ใน C ++ 03 เท็มเพลตอาร์กิวเมนต์ไม่สามารถมีการเชื่อมโยงภายใน

นี้ได้รับการคุ้มครองอย่างในทำไมมาตรฐาน :: เรียงลำดับไม่ยอมรับการเรียนการเปรียบเทียบประกาศภายในฟังก์ชั่น ดังนั้นรหัสต่อไปนี้ไม่ควรทำงานใน C ++ 03:

#include <iostream>
#include <vector>
#include <algorithm>

class Comparators
{
public:
    bool operator()(int first, int second)
    {
        return first < second;
    }
};

int main()
{
    class ComparatorsInner : public Comparators{};

    std::vector<int> compares ;
    compares.push_back(20) ;
    compares.push_back(10) ;
    compares.push_back(30) ;

    ComparatorsInner comparatorInner;
    std::sort(compares.begin(), compares.end(), comparatorInner);

    std::vector<int>::iterator it;
    for(it = compares.begin(); it != compares.end(); ++it)
    {
        std::cout << (*it) << std::endl;
    }
}

แต่ขณะนี้clangช่วยให้รหัสนี้ใน C ++ 03 โหมดที่มีคำเตือนถ้าคุณใช้-pedantic-errorsธงซึ่งเป็นชนิดของเหนอะเห็นมันมีชีวิตอยู่

8. >> จะไม่ถูกสร้างขึ้นอีกต่อไปเมื่อปิดเทมเพลตหลาย ๆ

การใช้>>เพื่อปิดเทมเพลตหลายรายการนั้นไม่ได้มีรูปแบบที่ไม่ดีอีกต่อไป แต่สามารถนำไปสู่โค้ดที่มีผลลัพธ์ต่างกันใน C ++ 03 และ C + 11 ตัวอย่างด้านล่างนำมาจากวงเล็บมุมฉากและความเข้ากันได้ย้อนหลัง :

#include <iostream>
template<int I> struct X {
  static int const c = 2;
};
template<> struct X<0> {
  typedef int c;
};
template<typename T> struct Y {
  static int const c = 3;
};
static int const c = 4;
int main() {
  std::cout << (Y<X<1> >::c >::c>::c) << '\n';
  std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}

และผลลัพธ์ใน C ++ 03 คือ:

0
3

และใน C ++ 11:

0
0

9. C ++ 11 เปลี่ยนแปลง std :: vector constructors บางส่วน

รหัสที่แก้ไขเล็กน้อยจากคำตอบนี้แสดงให้เห็นว่าการใช้ตัวสร้างต่อไปนี้จากstd :: vector :

std::vector<T> test(1);

สร้างผลลัพธ์ที่แตกต่างใน C ++ 03 และ C ++ 11:

#include <iostream>
#include <vector>

struct T
{
    bool flag;
    T() : flag(false) {}
    T(const T&) : flag(true) {}
};


int main()
{
    std::vector<T> test(1);
    bool is_cpp11 = !test[0].flag;

    std::cout << is_cpp11 << std::endl ;
}

10. การ จำกัด การแปลงใน initializers รวม

ใน C ++ 11 การแปลงที่แคบลงใน initializers รวมนั้นเป็นรูปแบบที่ไม่ดีและดูเหมือนว่าgccจะอนุญาตให้ทำได้ทั้งใน C ++ 11 และ C ++ 03 แม้ว่าจะมีคำเตือนตามค่าเริ่มต้นใน C ++ 11:

int x[] = { 2.0 };

สิ่งนี้ครอบคลุมอยู่ในร่างมาตรฐาน C ++ 11 ส่วน8.5.4 การเริ่มต้นรายการย่อหน้า3 :

รายการเริ่มต้นของวัตถุหรือการอ้างอิงประเภท T ถูกกำหนดดังนี้

และมีสัญลักษณ์แสดงหัวข้อต่อไปนี้ ( เหมืองของการเน้น ):

มิฉะนั้นถ้า T เป็นประเภทคลาสจะพิจารณาตัวสร้าง ตัวสร้างที่ใช้งานได้จะถูกระบุและสิ่งที่ดีที่สุดจะถูกเลือกผ่านการแก้ปัญหาการโหลดเกิน (13.3, 13.3.1.7) หากจำเป็นต้องทำการแปลงให้แคบลง (ดูด้านล่าง) เพื่อแปลงข้อโต้แย้งใด ๆ โปรแกรมนั้นจะมีรูปแบบไม่ดี

นี้และอื่น ๆ อีกมากมายเช่นได้รับความคุ้มครองในร่าง c ++ มาตรฐานส่วนannex C.2 C ++ และ ISO c ++ 2003 รวมถึง:

  • ตัวอักษรสตริงชนิดใหม่ [... ] โดยเฉพาะแมโครชื่อ R, u8, u8R, u, uR, U, UR หรือ LR จะไม่ถูกขยายเมื่ออยู่ติดกับตัวอักษรสตริง แต่จะตีความเป็นส่วนหนึ่งของตัวอักษรสตริง . ตัวอย่างเช่น

    #define u8 "abc"
    const char *s = u8"def"; // Previously "abcdef", now "def"
    
  • การสนับสนุนสตริงตัวอักษรที่ผู้ใช้กำหนด [... ] ก่อนหน้านี้ # 1 จะประกอบด้วยโทเค็นการประมวลผลล่วงหน้าสองชุดแยกกันและแมโคร _x จะถูกขยาย ในมาตรฐานสากลฉบับนี้ # 1 ประกอบด้วยโทเค็นการประมวลผลล่วงหน้าเพียงครั้งเดียวดังนั้นจึงไม่ได้ขยายมาโคร

    #define _x "there"
    "hello"_x // #1
  • ระบุการปัดเศษสำหรับผลลัพธ์ของรหัสจำนวนเต็ม / และ% [... ] 2003 ที่ใช้การหารจำนวนเต็มปัดผลลัพธ์เป็น 0 หรือไปทางอนันต์ลบในขณะที่มาตรฐานสากลนี้ปัดผลลัพธ์เป็น 0 เสมอ

  • ความซับซ้อนของขนาดสมาชิก () ฟังก์ชั่นสมาชิกคงที่ [... ] การใช้งานคอนเทนเนอร์บางอย่างที่สอดคล้องกับ C ++ 2003 อาจไม่สอดคล้องกับข้อกำหนดขนาด () ที่ระบุในมาตรฐานสากลนี้ การปรับคอนเทนเนอร์เช่นรายการ std :: เป็นข้อกำหนดที่เข้มงวดอาจต้องการการเปลี่ยนแปลงที่เข้ากันไม่ได้

  • เปลี่ยนคลาสพื้นฐานของ std :: ios_base :: failure [... ] std :: ios_base :: failure ไม่ได้มาโดยตรงจาก std :: exception อีกต่อไป แต่ตอนนี้ได้มาจาก std :: system_error ซึ่งได้มาจาก มาตรฐาน :: runtime_error รหัส C ++ 2003 ที่ถูกต้องซึ่งถือว่า std :: ios_base :: failure ได้มาโดยตรงจาก std :: exception อาจทำงานแตกต่างกันในมาตรฐานสากลนี้


ดังนั้นตัวอย่างส่วนใหญ่จึงแคบลงไปถึงความจริงที่ว่าพฤติกรรมที่ไม่ได้กำหนดไว้ก่อนหน้านี้ได้ถูกนิยามไว้อย่างดีแล้ว
MatthiasB

@MatthiasB 2, 3 และ 4 เป็นเรื่องเกี่ยวกับเรื่องนี้ดังนั้น ณ จุดนี้พวกเขาไม่ได้เป็นตัวอย่างส่วนใหญ่อีกต่อไป ฉันสงสัยว่าฉันจะพบตัวอย่างพฤติกรรมที่ไม่ได้กำหนดอีกมากมายดังนั้นเมื่อฉันเพิ่มมากขึ้นพวกเขาจะกลายเป็นชุดที่เล็กกว่า
Shafik Yaghmour

พฤติกรรม # 1 ไม่ได้ระบุดังนั้นฉันจึงนับว่าเป็นพฤติกรรมที่ไม่ได้กำหนด (อย่างน้อยคุณไม่สามารถคาดหวังว่าจะได้รับผลลัพธ์ที่เฉพาะเจาะจงด้วย c ++ 03 ตอนนี้คุณสามารถใช้ c ++ 11 ได้) # 5 ใช้ non- ส่วนขยายมาตรฐานของ c ++ แต่ฉันเดาว่าคุณพูดถูก ยิ่งคุณค้นหามากเท่าไรคุณก็ยิ่งพบตัวอย่างมากขึ้นซึ่งกำหนดไว้ในมาตรฐานทั้งสอง แต่ให้ผลลัพธ์ที่แตกต่างกัน
MatthiasB

@ Matiasias B ทั้งพฤติกรรมที่ไม่ระบุและไม่ได้กำหนดมีผลลัพธ์ที่ไม่พึงประสงค์ สำหรับส่วนขยายที่พิจารณาลีนุกซ์นั้นขึ้นอยู่กับจำนวนของ gcc extension ที่เราควรพิจารณาในโลกแห่งความเป็นจริง ฉันไม่ได้คาดหวังว่าจะพบตัวอย่างมากมายเมื่อฉันตอบคำถามนี้ครั้งแรก
Shafik Yaghmour

35

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

ลองพิจารณาตัวอย่างนี้ (ใช้boost::shared_ptrเพื่อให้ถูกต้อง C ++ 03):

#include <deque>
#include <iostream>

#include "boost/shared_ptr.hpp"


struct Widget
{
  boost::shared_ptr<int> p;

  Widget() : p(new int(42)) {}
};


int main()
{
  std::deque<Widget> d(10);
  for (size_t i = 0; i < d.size(); ++i)
    std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n';
}

C ++ 03 ตัวอย่างสด

C ++ 11 ตัวอย่างสด

เหตุผลก็คือ C ++ 03 ระบุโอเวอร์โหลดหนึ่งรายการสำหรับทั้ง "ขนาดระบุและองค์ประกอบต้นแบบ" และ "ระบุขนาดเท่านั้น" เช่นนี้ (อาร์กิวเมนต์ตัวจัดสรรจัดสรรถูกละเว้นเนื่องจากความกะทัดรัด):

container(size_type size, const value_type &prototype = value_type());

สิ่งนี้จะคัดลอกprototypeลงในคอนเทนเนอร์sizeครั้ง เมื่อเรียกใช้เพียงอาร์กิวเมนต์เดียวมันจะสร้างsizeสำเนาขององค์ประกอบที่สร้างขึ้นเป็นค่าเริ่มต้น

ใน C ++ 11 ลายเซ็นตัวสร้างนี้ถูกลบและแทนที่ด้วย overload สองตัวนี้:

container(size_type size);

container(size_type size, const value_type &prototype);

อันที่สองทำงานเหมือนก่อนสร้างsizeสำเนาของprototypeองค์ประกอบ อย่างไรก็ตามอันแรก (ซึ่งตอนนี้จัดการการโทรด้วยอาร์กิวเมนต์ขนาดที่ระบุเท่านั้น) เริ่มต้นสร้างแต่ละองค์ประกอบแต่ละรายการ

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


3
แม้ว่านี่จะเป็นการเปลี่ยนแปลงที่เกิดขึ้นอย่างชัดเจน แต่ฉันชอบพฤติกรรมของ C ++ 11 ฉันคาดหวังว่าสิ่งนี้จะส่งผลให้มีdequeสิบวิดเจ็ตที่แยกต่างหากถือสิบวิดเจ็ตที่ใช้ทรัพยากรร่วมกันไม่ได้
Agentlien

19

ผลลัพธ์ของการอ่านที่ล้มเหลวจากการstd::istreamเปลี่ยนแปลง CppReferenceสรุปว่า:

หากการแยกล้มเหลว (เช่นหากป้อนตัวอักษรในตำแหน่งที่คาดว่าจะมีตัวเลข) valueจะไม่มีการแก้ไขและfailbitตั้งค่าไว้ (จนถึง C ++ 11)

หากการแยกล้มเหลวจะมีการเขียนค่าศูนย์valueและfailbitตั้งค่าไว้ หากผลการสกัดในค่าที่มีขนาดใหญ่หรือเล็กเกินไปที่จะใส่ในvalue, std::numeric_limits<T>::max()หรือstd::numeric_limits<T>::min()เป็นลายลักษณ์อักษรและfailbitธงเป็นชุด (ตั้งแต่ C ++ 11)

นี่เป็นปัญหาหลักหากคุณคุ้นเคยกับความหมายใหม่แล้วต้องเขียนโดยใช้ C ++ 03 ต่อไปนี้ไม่ใช่แนวปฏิบัติที่ดีโดยเฉพาะ แต่กำหนดไว้อย่างดีใน C ++ 11:

int x, y;
std::cin >> x >> y;
std::cout << x + y;

อย่างไรก็ตามใน C ++ 03 โค้ดด้านบนใช้ตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นและดังนั้นจึงมีพฤติกรรมที่ไม่ได้กำหนด


4
คุณอาจจะเพิ่มว่าใน C ++ 03 หนึ่งจะได้ใช้นี้พฤติกรรมที่เป็นมาตรฐานint x = 1, y = 1; cin >> x >> y; cout << x*y;เพื่อให้ค่าเริ่มต้นในขณะที่ ด้วย C ++ 03 สิ่งนี้จะสร้างขึ้นอย่างถูกต้องxเมื่อไม่yสามารถอ่านได้
cmaster - คืนสถานะโมนิกา

15

เธรดนี้มีความแตกต่างอะไรบ้างระหว่าง C ++ 03 และ C ++ 0x ที่สามารถตรวจพบได้ในขณะใช้งานมีตัวอย่าง (คัดลอกมาจากเธรดนั้น) เพื่อกำหนดความแตกต่างของภาษาตัวอย่างเช่นโดยใช้ประโยชน์จากการยุบ C ++ 11:

template <class T> bool f(T&) {return true; } 
template <class T> bool f(...){return false;} 

bool isCpp11() 
{
    int v = 1;
    return f<int&>(v); 
}

และ c ++ 11 อนุญาตให้ใช้ชนิดโลคัลเป็นพารามิเตอร์เทมเพลต:

template <class T> bool cpp11(T)  {return true;} //T cannot be a local type in C++03
                   bool cpp11(...){return false;}

bool isCpp0x() 
{
   struct local {} var; //variable with local type
   return cpp11(var);
}

7

นี่เป็นอีกตัวอย่าง:

#include <iostream>

template<class T>
struct has {
  typedef char yes;
  typedef yes (&no)[2];    
  template<int> struct foo;    
  template<class U> static yes test(foo<U::bar>*);      
  template<class U> static no  test(...);    
  static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};

enum foo { bar };

int main()
{
    std::cout << (has<foo>::value ? "yes" : "no") << std::endl;
}

พิมพ์:

Using c++03: no
Using c++11: yes

เห็นผลลัพธ์ใน Coliru

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.