เคยมีการเปลี่ยนแปลงพฤติกรรมเงียบใน C ++ ด้วยเวอร์ชันมาตรฐานใหม่หรือไม่?


104

(ฉันกำลังมองหาตัวอย่างหรือสองตัวอย่างเพื่อพิสูจน์ประเด็นไม่ใช่รายการ)

เคยมีกรณีหรือไม่ที่การเปลี่ยนแปลงในมาตรฐาน C ++ (เช่นจาก 98 เป็น 11, 11 เป็น 14 เป็นต้น) เปลี่ยนพฤติกรรมของรหัสผู้ใช้ที่มีอยู่รูปแบบที่ดีและมีพฤติกรรมที่กำหนด - โดยไม่โต้ตอบ? เช่นไม่มีคำเตือนหรือข้อผิดพลาดเมื่อคอมไพล์กับเวอร์ชันมาตรฐานที่ใหม่กว่า?

หมายเหตุ:

  • ฉันกำลังถามเกี่ยวกับพฤติกรรมที่ได้รับคำสั่งตามมาตรฐานไม่ใช่เกี่ยวกับตัวเลือกผู้ดำเนินการ / ผู้เขียนคอมไพเลอร์
  • ยิ่งสร้างรหัสน้อยเท่าไหร่ก็ยิ่งดี (เป็นคำตอบสำหรับคำถามนี้)
  • ฉันไม่ได้หมายถึงรหัสที่มีการตรวจจับเวอร์ชันเช่น#if __cplusplus >= 201103L.
  • คำตอบเกี่ยวกับโมเดลหน่วยความจำนั้นใช้ได้

ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Samuel Liew

3
ฉันไม่เข้าใจว่าทำไมคำถามนี้จึงถูกปิด " เคยมีการเปลี่ยนแปลงพฤติกรรมเงียบใน C ++ ด้วยเวอร์ชันมาตรฐานใหม่ ๆ หรือไม่ " ดูเหมือนจะเน้นอย่างสมบูรณ์แบบและดูเหมือนว่าเนื้อหาของคำถามจะไม่ห่างหายไปจากสิ่งนั้น
Ted Lyngmo

autoในใจของฉันการเปลี่ยนแปลงที่ใหญ่ที่สุดทำลายความเงียบคือนิยามใหม่ของ ก่อน C ++ 11 auto x = ...;ประกาศintไฟล์. หลังจากนั้นก็ประกาศว่าอะไร...คือ
Raymond Chen

@RaymondChen: การเปลี่ยนแปลงนี้จะเงียบก็ต่อเมื่อคุณกำหนด int โดยปริยาย แต่บอกอย่างชัดเจนว่าautoตัวแปรคือ-type ฉันคิดว่าคุณคงสามารถนับจำนวนคนในโลกที่จะเขียนโค้ดแบบนั้นได้ในแง่หนึ่งยกเว้นการแข่งขันรหัส C ที่สับสน ...
einpoklum

จริงอยู่ที่พวกเขาเลือกมัน แต่มันเป็นการเปลี่ยนแปลงความหมายครั้งใหญ่
Raymond Chen

คำตอบ:


113

ประเภทการส่งคืนของstring::dataการเปลี่ยนแปลงจากconst char*เป็นchar*ใน C ++ 17 นั่นอาจสร้างความแตกต่างได้อย่างแน่นอน

void func(char* data)
{
    cout << data << " is not const\n";
}

void func(const char* data)
{
    cout << data << " is const\n";
}

int main()
{
    string s = "xyz";
    func(s.data());
}

มีการสร้างขึ้นเล็กน้อย แต่โปรแกรมทางกฎหมายนี้จะเปลี่ยนผลลัพธ์จาก C ++ 14 เป็น C ++ 17


7
โอ้ฉันไม่รู้ด้วยซ้ำว่ามีstd::stringการเปลี่ยนแปลงสำหรับ C ++ 17 ถ้ามีอะไรฉันคงคิดว่าการเปลี่ยนแปลง C ++ 11 อาจทำให้พฤติกรรมเงียบเปลี่ยนไป +1.
einpoklum

9
สร้างขึ้นหรือไม่สิ่งนี้แสดงให้เห็นถึงการเปลี่ยนแปลงโค้ดที่มีรูปแบบค่อนข้างดี
David C. Rankin

ในทางกลับกันการเปลี่ยนแปลงจะขึ้นอยู่กับกรณีการใช้งานที่ตลก แต่ถูกต้องตามกฎหมายเมื่อคุณเปลี่ยนเนื้อหาของ std :: string ในแหล่งกำเนิดโดยอาจใช้ฟังก์ชันดั้งเดิมที่ทำงานบนถ่าน * ตอนนี้ถูกต้องตามกฎหมายโดยสิ้นเชิง: เช่นเดียวกับเวกเตอร์มีการรับประกันว่ามีอาร์เรย์พื้นฐานที่ติดกันซึ่งคุณสามารถจัดการได้ (คุณสามารถทำได้ผ่านการอ้างอิงที่ส่งคืนเสมอตอนนี้มันทำให้เป็นธรรมชาติและชัดเจนมากขึ้น) กรณีการใช้งานที่เป็นไปได้คือชุดข้อมูลที่มีความยาวคงที่ที่สามารถแก้ไขได้ (เช่นข้อความบางประเภท) ซึ่งหากเป็นไปตาม std :: container จะคงบริการของ STL ไว้เช่นการจัดการเวลาชีวิตการคัดลอกเป็นต้น
Peter - Reinstate Monica

81

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

std::vector<Something> s(10);

C ++ 03 เริ่มต้นสร้างวัตถุชั่วคราวของประเภทองค์ประกอบSomethingและคัดลอกสร้างแต่ละองค์ประกอบในเวกเตอร์จากชั่วคราวนั้น

C ++ 11 เริ่มต้นสร้างแต่ละองค์ประกอบในเวกเตอร์

ในหลาย ๆ กรณี (ส่วนใหญ่?) ส่งผลให้เกิดสถานะสุดท้ายที่เท่าเทียมกัน แต่ไม่มีเหตุผลที่พวกเขาต้อง ขึ้นอยู่กับการใช้งานตัวสร้างSomethingเริ่มต้น / คัดลอกของ

ดูตัวอย่างที่สร้างขึ้นนี้ :

class Something {
private:
    static int counter;

public:
    Something() : v(counter++) {
        std::cout << "default " << v << '\n';
    }

    Something(Something const & other) : v(counter++) {
        std::cout << "copy " << other.v << " to " << v << '\n';
    }

    ~Something() {
        std::cout << "dtor " << v << '\n';
    }

private:
    int v;
};

int Something::counter = 0;

C ++ 03 จะเริ่มต้นสร้างหนึ่งSomethingมีv == 0สิบแล้วคัดลอกสร้างเพิ่มเติมจากที่หนึ่ง ในตอนท้ายเวกเตอร์ประกอบด้วยวัตถุสิบชิ้นที่มีvค่า 1 ถึง 10 รวมอยู่ด้วย

C ++ 11 จะเริ่มต้นสร้างแต่ละองค์ประกอบ ไม่มีการทำสำเนา ในตอนท้ายเวกเตอร์ประกอบด้วยวัตถุ 10 ชิ้นที่มีvค่า 0 ถึง 9 รวมอยู่ด้วย


@einpoklum ฉันได้เพิ่มตัวอย่างที่ออกแบบไว้แล้ว :)
cdhowie

3
ฉันไม่คิดว่ามันถูกสร้างขึ้น ตัวสร้างที่แตกต่างกันมักจะทำหน้าที่ต่างกันเช่นพูดการจัดสรรหน่วยความจำ คุณเพิ่งแทนที่เอฟเฟกต์ด้านหนึ่งด้วยอีกอันหนึ่ง (I / O)
einpoklum

17
@cdhowie ไม่ได้สร้างขึ้นเลย ฉันเพิ่งทำงานกับคลาส UUID ตัวสร้างเริ่มต้นสร้าง UUID แบบสุ่ม ฉันไม่รู้เกี่ยวกับความเป็นไปได้นี้ฉันเพิ่งสันนิษฐานพฤติกรรม C ++ 11
จอห์น

5
ตัวอย่างหนึ่งที่ใช้กันอย่างแพร่หลายในโลกจริงของการเรียนที่นี้จะมีความสำคัญเป็น cv::matOpenCV ตัวสร้างเริ่มต้นจะจัดสรรหน่วยความจำใหม่ในขณะที่ตัวสร้างการคัดลอกจะสร้างมุมมองใหม่ให้กับหน่วยความจำที่มีอยู่
jpa

ฉันจะไม่เรียกสิ่งนั้นว่าตัวอย่างที่สร้างขึ้นมามันแสดงให้เห็นถึงความแตกต่างของพฤติกรรมอย่างชัดเจน
David Waterworth

51

มาตรฐานมีรายชื่อของการเปลี่ยนแปลงที่จะหมดในภาคผนวกค [ต่าง] การเปลี่ยนแปลงหลายอย่างเหล่านี้สามารถนำไปสู่การเปลี่ยนแปลงพฤติกรรมเงียบ

ตัวอย่าง:

int f(const char*); // #1
int f(bool);        // #2

int x = f(u8"foo"); // until C++20: calls #1; since C++20: calls #2

7
@einpoklum อย่างน้อยก็มีหลายสิบคนที่กล่าวว่า "เปลี่ยนความหมาย" ของโค้ดที่มีอยู่หรือทำให้ "ดำเนินการต่างออกไป"
cpplearner

4
คุณจะสรุปเหตุผลของการเปลี่ยนแปลงนี้อย่างไร
นายูกิ

4
@ นายูกิค่อนข้างแน่ใจว่าการใช้boolเวอร์ชันนี้ไม่ใช่การเปลี่ยนแปลงที่ตั้งใจไว้เป็นเพียงผลข้างเคียงของกฎการแปลงอื่น ๆ ความตั้งใจจริงที่จะหยุดบางส่วนของความสับสนระหว่างการเข้ารหัสตัวอักษรการเปลี่ยนแปลงที่เกิดขึ้นจริงที่ถูกu8ตัวอักษรที่ใช้ในการให้แต่ตอนนี้ให้const char* const char8_t*
leftaround ประมาณ

25

ทุกครั้งที่เพิ่มวิธีการใหม่ ๆ (และมักจะทำหน้าที่) ในไลบรารีมาตรฐานจะเกิดขึ้น

สมมติว่าคุณมีไลบรารีมาตรฐาน:

struct example {
  void do_stuff() const;
};

ค่อนข้างเรียบง่าย ในการแก้ไขมาตรฐานบางอย่างจะมีการเพิ่มวิธีการใหม่หรือการโอเวอร์โหลดหรือถัดจากสิ่งใด ๆ :

struct example {
  void do_stuff() const;
  void method(); // a new method
};

สิ่งนี้สามารถเปลี่ยนลักษณะการทำงานของโปรแกรม C ++ ที่มีอยู่ได้อย่างเงียบ ๆ

เนื่องจากความสามารถในการสะท้อนที่ จำกัด ของ C ++ ในขณะนี้เพียงพอที่จะตรวจจับว่ามีวิธีการดังกล่าวอยู่หรือไม่และเรียกใช้รหัสที่แตกต่างกันตาม

template<class T, class=void>
struct detect_new_method : std::false_type {};

template<class T>
struct detect_new_method< T, std::void_t< decltype( &T::method ) > > : std::true_type {};

นี่เป็นเพียงวิธีที่ค่อนข้างง่ายในการตรวจจับสิ่งใหม่ ๆmethodมีหลายวิธี

void task( std::false_type ) {
  std::cout << "old code";
};
void task( std::true_type ) {
  std::cout << "new code";
};

int main() {
  task( detect_new_method<example>{} );
}

สิ่งเดียวกันนี้สามารถเกิดขึ้นได้เมื่อคุณลบเมธอดออกจากคลาส

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

มาตรฐานไปและเพิ่ม.data()วิธีการลงในคอนเทนเนอร์และทันใดนั้นประเภทก็เปลี่ยนเส้นทางที่ใช้สำหรับการทำให้เป็นอนุกรม

มาตรฐาน C ++ ทั้งหมดสามารถทำได้หากไม่ต้องการหยุดการทำงานก็คือการทำให้รหัสที่แตกอย่างเงียบ ๆ นั้นหายากหรือไม่มีเหตุผล


3
ฉันควรตั้งคำถามเพื่อยกเว้น SFINAE เพราะนี่ไม่ใช่สิ่งที่ฉันหมายถึง ... แต่ใช่นั่นเป็นความจริงดังนั้น +1
einpoklum

"เรื่องแบบนี้เกิดขึ้นโดยทางอ้อม" ส่งผลให้มีการโหวตเพิ่มคะแนนโหวตลดลงเนื่องจากเป็นกับดักที่แท้จริง
Ian Ringrose

1
นี่เป็นตัวอย่างที่ดีจริงๆ แม้ว่า OP จะหมายถึงการยกเว้น แต่นี่อาจเป็นหนึ่งในสิ่งที่เป็นไปได้มากที่สุดที่จะทำให้เกิดการเปลี่ยนแปลงพฤติกรรมเงียบกับโค้ดที่มีอยู่ +1
cdhowie

1
@TedLyngmo หากคุณไม่สามารถแก้ไขตัวตรวจจับได้ให้เปลี่ยนสิ่งที่ตรวจพบ การแม่นปืนเท็กซัส!
Yakk - Adam Nevraumont

15

โอ้เด็ก ... เชื่อมโยง cpplearnerที่จัดไว้ให้เป็นที่น่ากลัว

ในบรรดาคนอื่น ๆ C ++ 20 ไม่อนุญาตการประกาศโครงสร้างแบบ C ของโครงสร้าง C ++

typedef struct
{
  void member_foo(); // Ill-formed since C++20
} m_struct;

หากคุณได้รับการสอนการเขียน structs เช่นนั้น (และคนที่สอน "C กับการเรียน" สอนว่าที่) คุณกำลังเมา



19
@ Peter-ReinstateMonica ฉันมักจะมีtypedefโครงสร้างของฉันและแน่นอนที่สุดว่าฉันจะไม่เสียชอล์กไปกับมัน นี่เป็นเรื่องของรสนิยมแน่นอนที่สุดและในขณะที่มีผู้มีอิทธิพลสูง (Torvalds ... ) ที่แบ่งปันมุมมองของคุณคนอื่น ๆ เช่นฉันจะชี้ให้เห็นว่าหลักการตั้งชื่อประเภทเป็นสิ่งที่จำเป็น การทำให้โค้ดยุ่งเหยิงด้วยstructคีย์เวิร์ดช่วยเพิ่มความเข้าใจเพียงเล็กน้อยว่าอักษรตัวใหญ่ ( MyClass* object = myClass_create();) จะไม่สื่อถึง ฉันเคารพถ้าคุณต้องการstructรหัสของคุณ แต่ฉันไม่ต้องการมันในของฉัน
cmaster - คืนสถานะ monica

5
ที่กล่าวว่าเมื่อเขียนโปรแกรม C ++ มันเป็นแบบแผนที่ดีที่จะใช้structสำหรับชนิดข้อมูลธรรมดาเท่านั้นและclassทุกอย่างที่มีฟังก์ชันสมาชิก แต่คุณไม่สามารถใช้การประชุมนั้นใน C เนื่องจากไม่มีclassใน C.
cmaster - คืนสถานะ monica

1
@ Peter-ReinstateMonica ใช่คุณไม่สามารถแนบเมธอดในภาษา C ได้ แต่นั่นไม่ได้หมายความว่า C structคือ POD จริงๆ วิธีที่ฉันเขียนโค้ด C โครงสร้างส่วนใหญ่จะสัมผัสด้วยโค้ดในไฟล์เดียวและตามฟังก์ชันที่มีชื่อคลาสเท่านั้น โดยพื้นฐานแล้ว OOP ไม่มีน้ำตาลที่เป็นประโยค สิ่งนี้ช่วยให้ฉันสามารถควบคุมสิ่งที่เปลี่ยนแปลงภายใน a ได้structจริงและมีการรับประกันค่าคงที่ระหว่างสมาชิก ดังนั้นฉันstructsมักจะมีฟังก์ชั่นของสมาชิกการใช้งานส่วนตัวค่าคงที่และนามธรรมจากสมาชิกข้อมูลของพวกเขา ฟังดูไม่เหมือน POD ใช่ไหม
cmaster - คืนสถานะโมนิกา

6
ตราบใดที่ไม่ได้รับอนุญาตในการextern "C"บล็อกฉันก็ไม่เห็นปัญหาใด ๆ กับการเปลี่ยนแปลงนี้ ไม่ควรมีใครพิมพ์นิยามโครงสร้างใน C ++ นี่ไม่ใช่อุปสรรคที่ใหญ่ไปกว่าการที่ C ++ มีความหมายต่างจาก Java เมื่อคุณเรียนรู้ภาษาโปรแกรมใหม่คุณอาจต้องเรียนรู้นิสัยใหม่ ๆ
โคดี้เกรย์

15

นี่คือตัวอย่างที่พิมพ์ 3 ใน C ++ 03 แต่ 0 ใน C ++ 11:

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

>>การเปลี่ยนแปลงในลักษณะการทำงานนี้มีสาเหตุมาจากการจัดการพิเศษสำหรับ ก่อนหน้า C ++ 11 >>เป็นตัวดำเนินการกะที่ถูกต้องเสมอ ด้วย C ++ 11 >>สามารถเป็นส่วนหนึ่งของการประกาศเทมเพลตได้เช่นกัน


ในทางเทคนิคแล้วนี่เป็นความจริง แต่รหัสนี้ "คลุมเครืออย่างไม่เป็นทางการ" ซึ่งเริ่มต้นด้วยเนื่องจากการใช้>>วิธีนั้น
einpoklum

11

Trigraphs หลุด

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

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

ข้อ จำกัด เพิ่มเติมเกี่ยวกับ char

มาตรฐานยังอ้างถึงชุดอักขระการดำเนินการซึ่งกำหนดไว้สำหรับการนำไปใช้งาน แต่ต้องมีชุดอักขระต้นทางทั้งหมดอย่างน้อยรวมทั้งรหัสควบคุมจำนวนเล็กน้อย

มาตรฐาน C ++ กำหนดcharเป็นชนิดอินทิกรัลที่อาจไม่ได้ลงนามซึ่งสามารถแทนค่าทุกค่าในชุดอักขระการดำเนินการได้อย่างมีประสิทธิภาพ ด้วยการเป็นตัวแทนจากทนายความด้านภาษาคุณสามารถโต้แย้งได้ว่า a charต้องมีอย่างน้อย 8 บิต

หากการใช้งานของคุณใช้ค่าที่ไม่ได้ลงชื่อcharคุณจะรู้ว่าค่านี้สามารถอยู่ในช่วง 0 ถึง 255 และเหมาะสำหรับการจัดเก็บค่าไบต์ที่เป็นไปได้ทั้งหมด

แต่ถ้าการใช้งานของคุณใช้ค่าที่มีลายเซ็นก็มีตัวเลือก

ส่วนใหญ่จะใช้ส่วนเติมเต็มสองค่าโดยให้charช่วงต่ำสุด -128 ถึง 127 นั่นคือ 256 ค่าที่ไม่ซ้ำกัน

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

ฉันไม่แน่ใจว่าคณะกรรมการได้กำหนดอย่างชัดเจนว่าสิ่งนี้เป็นข้อบกพร่อง แต่เป็นเพราะคุณไม่สามารถพึ่งพามาตรฐานในการรับประกันการเดินทางไปกลับจากunsigned charไปcharและกลับจะรักษาคุณค่าดั้งเดิมไว้ได้ (ในทางปฏิบัติการนำไปใช้งานทั้งหมดเกิดจากการใช้ส่วนประกอบสองอย่างสำหรับประเภทอินทิกรัลที่ลงนาม

เมื่อเร็ว ๆ นี้ (C ++ 17?) ได้รับการแก้ไขถ้อยคำเพื่อให้แน่ใจว่าการสะดุดรอบ การแก้ไขนั้นพร้อมกับข้อกำหนดอื่น ๆ ทั้งหมดในการcharกำหนดส่วนเติมเต็มของทั้งสองอย่างมีประสิทธิภาพสำหรับการลงนามcharโดยไม่ต้องพูดอย่างชัดเจน (แม้ว่ามาตรฐานจะยังคงอนุญาตให้ใช้การแสดงเครื่องหมาย + ขนาดสำหรับอินทิกรัลประเภทอื่น ๆ ที่มีการลงนาม) มีข้อเสนอที่กำหนดให้ประเภทอินทิกรัลที่ลงนามทั้งหมดใช้ส่วนเติมเต็มของทั้งสอง แต่ฉันจำไม่ได้ว่ามันทำให้เป็น C ++ 20 หรือไม่

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


ส่วนของ Trigraphs ไม่ใช่คำตอบสำหรับคำถามนี้นั่นไม่ใช่การเปลี่ยนแปลงแบบเงียบ ๆ และ IIANM ส่วนที่สองคือการเปลี่ยนแปลงของการนำไปใช้ที่กำหนดให้เป็นพฤติกรรมที่ได้รับคำสั่งอย่างเคร่งครัดซึ่งไม่ใช่สิ่งที่ฉันถามด้วย
einpoklum

10

ฉันไม่แน่ใจว่าคุณคิดว่านี่เป็นการเปลี่ยนแปลงโค้ดที่ถูกต้องหรือไม่ แต่ ...

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

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


1
ฉันคิดว่า "ข้อกำหนด" นี้ถูกเพิ่มใน C ++ 17 ไม่ใช่ C ++ 11? (ดูการปรากฏตัวชั่วคราว )
cdhowie

@cdhowie: ฉันคิดว่าคุณพูดถูก ฉันไม่ได้มีมาตรฐานโดยตรงเมื่อฉันเขียนสิ่งนี้และฉันอาจไว้วางใจมากเกินไปกับผลการค้นหาของฉัน
Adrian McCarthy

การเปลี่ยนแปลงพฤติกรรมที่กำหนดการนำไปใช้งานจะไม่นับเป็นคำตอบสำหรับคำถามนี้
einpoklum

7

พฤติกรรมเมื่ออ่านข้อมูล (ตัวเลข) จากสตรีมและการอ่านล้มเหลวมีการเปลี่ยนแปลงตั้งแต่ c ++ 11

ตัวอย่างเช่นการอ่านจำนวนเต็มจากสตรีมในขณะที่ไม่มีจำนวนเต็ม:

#include <iostream>
#include <sstream>

int main(int, char **) 
{
    int a = 12345;
    std::string s = "abcd";         // not an integer, so will fail
    std::stringstream ss(s);
    ss >> a;
    std::cout << "fail = " << ss.fail() << " a = " << a << std::endl;        // since c++11: a == 0, before a still 12345 
}

เนื่องจาก c ++ 11 จะตั้งค่าจำนวนเต็มการอ่านเป็น 0 เมื่อล้มเหลว ที่ c ++ <11 จำนวนเต็มไม่เปลี่ยนแปลง ที่กล่าวว่า gcc แม้ว่าจะบังคับให้มาตรฐานกลับไปที่ c ++ 98 (ด้วย -std = c ++ 98) จะแสดงพฤติกรรมใหม่เสมออย่างน้อยตั้งแต่เวอร์ชัน 4.4.7

(อิมโฮพฤติกรรมเก่าดีขึ้นจริง: ทำไมเปลี่ยนค่าเป็น 0 ซึ่งมันถูกต้องโดยตัวมันเองเมื่ออ่านอะไรไม่ได้)

ข้อมูลอ้างอิง: ดูhttps://en.cppreference.com/w/cpp/locale/num_get/get


แต่ไม่มีการเปลี่ยนแปลงที่กล่าวถึงเกี่ยวกับ returnType มีเพียง 2 ข่าวที่มากเกินไปตั้งแต่ C ++ 11
สร้างสำเร็จ

พฤติกรรมนี้ถูกกำหนดไว้ทั้งใน C ++ 98 และใน C ++ 11 หรือไม่ หรือมีการกำหนดพฤติกรรมหรือไม่?
einpoklum

เมื่อ cppreference.com ถูก: "ถ้าเกิดข้อผิดพลาด v จะไม่เปลี่ยนแปลง (จนถึง C ++ 11)" ดังนั้นพฤติกรรมจึงถูกกำหนดก่อน C ++ 11 และเปลี่ยนไป
DanRechtsaf

ตามความเข้าใจของฉันพฤติกรรมสำหรับ ss> a ถูกกำหนดไว้อย่างแน่นอน แต่สำหรับกรณีทั่วไปที่คุณกำลังอ่านตัวแปรที่ไม่ได้เริ่มต้นพฤติกรรม c ++ 11 จะใช้ตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นซึ่งเป็นพฤติกรรมที่ไม่ได้กำหนด ดังนั้นการสร้างค่าเริ่มต้นบนความล้มเหลวจะป้องกันพฤติกรรมที่ไม่ได้กำหนดโดยทั่วไป
Rasmus Damgaard Nielsen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.