อาร์เรย์ความยาวเป็นศูนย์แบบไดนามิกใน C ++

#include <stdlib.h>

void *operator new[](size_t size, int n){
    if( size != 0 && n != 0 )
        return calloc(n, size);
    return calloc(1, 1);
}

int main(){

    int * p1;
    const int i = 0;

//  p1 = new (20)  int[i] ; // Case 1 (OK)
    p1 = new (20) (int[i]); // Case 2 (Warning)

    if( p1 == 0 )
        return 1;
    return 0;
}

โค้ดนี้ (https://godbolt.org/g/hjo7Xn) คอมไพล์ได้สำเร็จด้วย Clang 6.0.0 อย่างไรก็ตาม GCC 7.3 ออกคำเตือนว่าอาร์เรย์ที่มีความยาวเป็นศูนย์เป็นสิ่งต้องห้ามใน C ++ หากถอดวงเล็บออก (กรณีที่ 1) คำเตือนจะหายไป

ไม่เหมือนกับอาร์เรย์ที่มีความยาวเป็นศูนย์ที่จัดสรรแบบคงที่ (C++03:8.3.4/1) อนุญาตให้ใช้อาร์เรย์ที่มีความยาวเป็นศูนย์ที่จัดสรรแบบไดนามิกได้ (C++03:5.3.4/6) อย่างไรก็ตาม ในมาตรฐาน C++ รูปแบบหลังจะได้รับอนุญาตอย่างชัดเจนก็ต่อเมื่อทำตามหนึ่งในสองเส้นทางไวยากรณ์ที่เป็นไปได้ของ นิพจน์ใหม่ นั่นคือเส้นทางที่มี new-type-id em> และไม่มีวงเล็บ (กรณีที่ 1)

มาตรฐาน C++ อนุญาตให้ใช้ นิพจน์ใหม่ กับอาร์เรย์ที่มีความยาวเป็นศูนย์ตามเส้นทางไวยากรณ์ที่สอง นั่นคือ ด้วย type-id และ วงเล็บ (กรณีที่ 2)?

คำพูดที่เกี่ยวข้องเพียงอย่างเดียวคือ C++03:5.3.4/5:

เมื่อวัตถุที่จัดสรรเป็นอาร์เรย์ (นั่นคือ มีการใช้ไวยากรณ์ direct-new-declarator หรือ new-type-id หรือ type-id em> หมายถึงประเภทอาร์เรย์) นิพจน์ใหม่ ให้ตัวชี้ไปยังองค์ประกอบเริ่มต้น (ถ้ามี) ของอาร์เรย์

ข้อความ (if any) จะอนุญาตให้ใช้อาร์เรย์ที่ไม่มีองค์ประกอบ อย่างไรก็ตาม ดูเหมือนจะไม่ชัดเจนว่าจะอ้างถึงทั้งสองกรณีหรือเฉพาะอาร์เรย์ที่มี new-type-id และไม่มีวงเล็บ (กรณีที่ 1)

ขอบคุณล่วงหน้า.

หมายเหตุ:

  1. ISO/IEC 14882:2003 ส่วนที่ 8.3.4 ย่อหน้าที่ 1:
    #P9#
  2. ISO/IEC 14882:2003 ส่วนที่ 5.3.4 ย่อหน้าที่ 6:
    #P10#
  3. ISO/IEC 14882:2003 ส่วนที่ 5.3.4 ย่อหน้าที่ 7:
    #P11#
  4. ISO/IEC 14882:2003 ส่วนที่ 5.3.4 ย่อหน้าที่ 1:
    #P12# #P13# #P14#
  5. แม้ว่าคำพูดข้างต้นมาจากมาตรฐาน C++03 เท่าที่ฉันทราบ ปัญหานี้ยังคงไม่ชัดเจนใน C++ Standard เวอร์ชันใหม่กว่า (C++11, C++14 และ C++17)
  6. โพสต์ที่น่าสนใจของ Herb Sutter เกี่ยวกับศูนย์ อาร์เรย์ความยาว
  7. โค้ดในตัวอย่างเป็นการทดสอบที่มีการปรับเปลี่ยนเล็กน้อยจากชุด SuperTest ของ SolidSands'

person José Luis    schedule 30.04.2018    source แหล่งที่มา
comment
แค่อยากรู้อยากเห็น: มีเหตุผลไหมว่าทำไมคุณถึงอ้างอิงมาตรฐาน C ++ 03 หากคุณต้องการการปฏิบัติตาม C ++ 11 เนื่องจากมีฉบับร่างต่างๆ ให้เลือกทางออนไลน์ ซึ่งอาจเสนอราคา/ลิงก์ได้ง่ายกว่า   -  person Rakete1111    schedule 30.04.2018
comment
มีอะไรอยู่ในรถบักกี้แบบกำหนดเอง operator new[]? มันเกี่ยวอะไรด้วยวะ?   -  person Cheers and hth. - Alf    schedule 30.04.2018
comment
ไม่มีคำเตือนจาก gcc 8   -  person Sam Varshavchik    schedule 30.04.2018
comment
@ Rakete1111 ฉันสนใจที่จะปฏิบัติตามตั้งแต่ C ++ 03 เป็นต้นไป แต่คุณพูดถูก แค่ลิงก์ไปที่ Standard แทนที่จะอ้างอิงโดยตรงจะดีกว่า หากฉันพบลิงก์ออนไลน์ตามส่วน (ไม่ใช่ PDF) C++03 Standard ฉันจะอัปเดตโพสต์   -  person José Luis    schedule 01.05.2018
comment
@Cheersandhth.-Alf ฉันคิดว่าปัญหาจะเหมือนเดิมฉันทิ้งมันไว้เพราะฉันไม่ต้องการแก้ไขการทดสอบดั้งเดิมมากนัก (ฉันลดมันลงในส่วนอื่นแล้ว) ทำไมมันถึงบั๊ก?   -  person José Luis    schedule 01.05.2018
comment
@ JoséLuis: ไม่รับประกันว่าจะส่งคืนอย่างน้อยตามจำนวนไบต์ที่ร้องขอหากส่งคืน   -  person Cheers and hth. - Alf    schedule 01.05.2018


คำตอบ (2)


ด้วยวงเล็บ คุณจะมี type-id ปกติ แทนที่จะเป็นไวยากรณ์พิเศษที่เรียกว่า new-type-id ที่รองรับขนาดอาร์เรย์แบบไดนามิก

ตั้งแต่วันที่ C++17 มาตรฐานไม่มีข้อกำหนดพิเศษสำหรับการใช้ type-id นี้ ดังนั้นคำถามจึงอยู่ที่ว่าคุณสามารถเขียนได้หรือไม่

auto main() -> int
{
    using Argh = int[0];
}

คุณไม่สามารถทำได้ เนื่องจากประเภทของ type-id ถูกกำหนดไว้ในแง่ของ “การประกาศสำหรับตัวแปรหรือฟังก์ชันประเภทนั้นที่ละเว้นชื่อของเอนทิตี” ที่สมมติขึ้น (C++17 §11.1/1) และสำหรับการประกาศตัวแปรอาร์เรย์ กฎคือ “หากมีนิพจน์คงที่ (8.20) อยู่ จะต้องเป็นนิพจน์คงที่ที่แปลงแล้วประเภท std::size_t และค่าของมันจะต้องมากกว่าศูนย์” (C+ +17 §11.3.4/1)


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

int x[42];

ไม่ถูกต้อง (แน่นอนว่าไม่ใช่) และจะต้องแสดงเป็น

int x[std::size_t(42)];

การกำหนดว่าอะไรเป็นการตีความที่สมเหตุสมผลหรือไม่เคยเป็นเรื่องง่าย ใครๆก็ถามได้ว่ามันสมเหตุสมผลไหม? ดังนั้นสำหรับกรณีข้างต้น คำตอบคือไม่ และใครๆ ก็สามารถละทิ้งความเป็นไปได้นั้นไปได้

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

person Cheers and hth. - Alf    schedule 01.05.2018

ไม่ ตัวพิมพ์ขนาดศูนย์ไม่สามารถใช้ type-id ในวงเล็บได้ ลักษณะการทำงานสำหรับอาร์เรย์ขนาด 0 ถูกกำหนดไว้ (ในแบบร่างปัจจุบัน) สำหรับ นิพจน์ ใน noptr-new-declarator เท่านั้น ([expr.new]/7) A new ที่มีประเภทวงเล็บจะพยายามสร้างวัตถุประเภทนั้น และไม่มีอาร์เรย์ขนาด 0 ([dcl.array]/1) ไม่ได้เป็น type-id ด้วยซ้ำ ([dcl.name]/1)

แน่นอนว่าอาร์เรย์ขนาดศูนย์นั้นเป็นส่วนขยายทั่วไป ดังนั้นผลลัพธ์ในทางปฏิบัติอาจแตกต่างกันไป

person Davis Herring    schedule 01.05.2018
comment
นอกจากนี้อาร์เรย์ที่จัดสรรแบบไดนามิกยังเป็นวัตถุอีกด้วย - person Cheers and hth. - Alf; 01.05.2018
comment
@Cheersandhth.-Alf: ใช่ แต่เรามีแนวคิดพิเศษใน /7 ที่ว่าเราสามารถ "จัดสรรอาร์เรย์ที่ไม่มีองค์ประกอบ" ได้ - person Davis Herring; 01.05.2018
comment
ฉันหมายถึงจะบอกว่าคำสั่ง “A new ที่มีประเภทวงเล็บพยายามสร้าง วัตถุ ประเภทนั้นนั้นไม่มีความหมาย เพราะการประเมินนิพจน์ new ทุกรายการ (สำเร็จ) จะสร้างวัตถุขึ้นมา - person Cheers and hth. - Alf; 01.05.2018
comment
@Cheersandhth.-Alf: new int[0] สร้างวัตถุอะไร? ไม่สามารถตั้งชื่อประเภทที่เหมาะสมเพียงประเภทเดียว (แม้จะผ่าน decltype) บางทีอาจกล่าวได้ว่ามีอยู่จริง แต่มันทำให้ “new มักจะทำให้วัตถุ” เป็นเรื่องที่น่าสงสัย - person Davis Herring; 01.05.2018
comment
มันคือ int[] ซึ่งเป็นที่รู้จักมากที่สุด ซึ่งเป็นกรณีของวัตถุที่สร้างโดย new int[argc] เช่นกัน - person Cheers and hth. - Alf; 01.05.2018
comment
@Cheersandhth.-Alf: ไม่มีการสร้างวัตถุประเภทที่ไม่สมบูรณ์ (ณ จุดนั้น) - person Davis Herring; 02.05.2018
comment
เมื่อคุณปฏิเสธว่าประเภทของอ็อบเจ็กต์อาเรย์ที่สร้างโดย new int[argc] นั้นไม่สมบูรณ์ คุณจะต้องเลิกใช้ประเภทที่สมบูรณ์ที่คุณคิดว่ามี หรือมิฉะนั้น เนื่องจากออบเจ็กต์ C++ ทุกรายการมีประเภท คุณจึงปฏิเสธว่าไม่มีออบเจ็กต์เลย แต่การให้ประเภทที่สมบูรณ์นั้นเป็นไปไม่ได้ เนื่องจากจะต้องทราบประเภท ณ เวลาคอมไพล์ และจะไม่ทราบค่า argc จนกว่าจะถึงรันไทม์ ดังนั้นสิ่งที่คุณยืนยันจึงเป็นไปไม่ได้ กล่าวอีกนัยหนึ่ง ห่วงโซ่ของการปฏิเสธนี้เข้าสู่ดินแดนที่เข้าใจผิด Q.E.D. - person Cheers and hth. - Alf; 02.05.2018