สามารถแปลง nullptr เป็น uintptr_t ได้หรือไม่ คอมไพเลอร์ไม่เห็นด้วย


10

พิจารณาโปรแกรมนี้:

#include <cstdint>
using my_time_t = uintptr_t;

int main() {
    const my_time_t t = my_time_t(nullptr);
}

ไม่สามารถคอมไพล์ด้วย msvc v19.24:

<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'

Compiler returned: 2

แต่เสียงดังกราว (9.0.1) และ gcc (9.2.1) "กิน" รหัสนี้โดยไม่มีข้อผิดพลาดใด ๆ

ฉันชอบพฤติกรรม MSVC แต่ได้รับการยืนยันตามมาตรฐานหรือไม่ กล่าวอีกนัยหนึ่งมันเป็นข้อผิดพลาดใน clang / gcc หรือเป็นไปได้ที่จะตีความมาตรฐานว่านี่เป็นพฤติกรรมที่ถูกต้องจาก gcc / clang?


2
ฉันอ่านสิ่งนี้เป็นการเริ่มต้นการคัดลอกจากการส่งแบบฟังก์ชัน จากนั้นคอมไพเลอร์จะถูกตีความว่าเป็นหนึ่งใน C ++ casts "แม้ว่าจะไม่สามารถคอมไพล์ได้" อาจมีความไม่สอดคล้องกันระหว่างคอมไพเลอร์ว่าการที่นักแสดงได้รับการตีความอย่างไร
wreckgar23 23

เท่าที่ฉันทราบ MSVC v19.24 ไม่สนับสนุนโหมด C ++ 11 คุณหมายถึง C ++ 14 หรือ C ++ 17 แทนหรือไม่
walnut

คำตอบ:


5

ในความคิดของฉัน MSVC ไม่ทำงานตามมาตรฐาน

ฉันกำลังอ้างอิงคำตอบนี้ใน C ++ 17 (ฉบับร่าง N4659) แต่ C ++ 14 และ C ++ 11 มีถ้อยคำที่เทียบเท่ากัน

my_time_t(nullptr)เป็นpostfix-expressionและเนื่องจากmy_time_tเป็นประเภทและ(nullptr)เป็นนิพจน์เดียวในรายการ initializer ที่เป็นวงเล็บ ( [expr.type.conv] / 2 )

หล่ออย่างชัดเจนพยายามไม่กี่ที่แตกต่างกันเฉพาะ c ++ ปลดเปลื้อง (ที่มีส่วนขยาย) reinterpret_castในยังโดยเฉพาะอย่างยิ่ง ( [expr.cast] /4.4 ) casts ที่ลองใช้ก่อนหน้าreinterpret_castนี้คือconst_castและstatic_cast(พร้อมกับส่วนขยายและรวมกัน) แต่ไม่มีรายการใดที่สามารถส่งstd::nullptr_tไปยังประเภทที่สมบูรณ์ได้

แต่reinterpret_cast<my_time_t>(nullptr)ควรประสบความสำเร็จเพราะ[expr.reinterpret.cast] / 4บอกว่าค่าประเภทstd::nullptr_tสามารถแปลงเป็นประเภทอินทิกรัลราวกับว่าโดยreinterpret_cast<my_time_t>((void*)0)ซึ่งเป็นไปได้เพราะmy_time_t = std::uintptr_tควรเป็นประเภทที่มีขนาดใหญ่พอที่จะเป็นตัวแทนของค่าตัวชี้ทั้งหมดและภายใต้เงื่อนไขนี้ ย่อหน้ามาตรฐานเดียวกันช่วยให้การแปลงvoid*เป็นประเภทหนึ่ง

เป็นเรื่องน่าประหลาดใจอย่างยิ่งที่ MSVC อนุญาตให้มีการแปลงหากใช้สัญลักษณ์แทนการใช้มากกว่าสัญกรณ์ใช้งาน:

const my_time_t t = (my_time_t)nullptr;

1
อ๋อ โปรดทราบว่าstatic_castโดยเฉพาะอย่างยิ่งมีบางกรณีที่ตั้งใจจะดักจับบันไดรูปตัว C (เช่นรูปแบบตัว C ไปยังฐานที่ไม่ชัดเจนนั้นเป็นรูปแบบที่ไม่ดีstatic_castแทนที่จะเป็นreinterpret_cast) แต่ไม่มีสิ่งใดที่นี่
TC

my_time_t(nullptr)เป็นไปตามคำจำกัดความเดียวกัน(my_time_t)nullptrดังนั้น MSVC จึงผิดที่จะยอมรับและปฏิเสธอีกฝ่ายหนึ่ง
Richard Smith

2

แม้ว่าฉันจะไม่พบการกล่าวถึงที่ชัดเจนในWorking Draft C ++ Standard (ตั้งแต่ปี 2014) ว่าการแปลงจากstd::nullptr_tเป็นประเภทหนึ่งเป็นสิ่งต้องห้าม แต่ก็ไม่มีการกล่าวถึงว่าอนุญาตให้มีการแปลงเช่นนี้!

อย่างไรก็ตามกรณีของการแปลงจากstd::nullptr_tถึงbool ถูกกล่าวถึงอย่างชัดเจน:

4.12 การแปลงบูลีนสามารถแปลง
ค่า prithue ของเลขคณิต, การแจงนับที่ไม่มีการกำหนดขอบเขต, ตัวชี้หรือตัวชี้ไปยังประเภทสมาชิกเป็น prvalue ของบูลประเภท ค่าศูนย์ค่าตัวชี้โมฆะหรือค่าตัวชี้สมาชิกโมฆะจะถูกแปลงเป็นเท็จ ค่าอื่นใดจะถูกแปลงเป็นจริง สำหรับ direct-initialization (8.5) prvalue ของ type std :: nullptr_t สามารถถูกแปลงเป็น prvalue ของ type bool; ค่าผลลัพธ์เป็นเท็จ

นอกจากนี้สถานที่เดียวในเอกสารฉบับร่างนี้ที่std::nullptr_tกล่าวถึงการแปลงจากประเภทที่รวมอยู่ในส่วน "reinterpret_cast":

5.2.10 ตีความการร่ายซ้ำ
...
(4) ตัวชี้สามารถแปลงได้อย่างชัดเจนเป็นชนิดอินทิกรัลที่ใหญ่พอที่จะเก็บไว้ ฟังก์ชั่นการทำแผนที่มีการกำหนดใช้งาน [หมายเหตุ: มันมีจุดมุ่งหมายที่จะแปลกใจสำหรับผู้ที่รู้โครงสร้างที่อยู่ของเครื่องต้นแบบ - หมายเหตุท้าย] ค่าประเภท std :: nullptr_t สามารถแปลงเป็นประเภทอินทิกรัล การแปลงมีความหมายและความถูกต้องเช่นเดียวกับการแปลง (void *) 0 เป็นประเภทอินทิกรัล [หมายเหตุ: reinterpret_cast ไม่สามารถใช้เพื่อแปลงค่าชนิดใด ๆ เป็นชนิด std :: nullptr_t - บันทึกท้าย]

ดังนั้นจากการสังเกตการณ์ทั้งสองนี้เราสามารถคาดการณ์ได้อย่างสมเหตุสมผลว่า IM MSVCคอมไพเลอร์นั้นถูกต้อง

แก้ไข : อย่างไรก็ตามการใช้ "สัญกรณ์การทำงานของคุณ" อาจแนะนำสิ่งที่ตรงกันข้าม! MSVCคอมไพเลอร์ไม่มีปัญหาในการใช้ C- โยนสไตล์ในตัวอย่างเช่น:

uintptr_t answer = (uintptr_t)(nullptr);

แต่ (ในรหัสของคุณ) มันบ่นเกี่ยวกับเรื่องนี้:

uintptr_t answer = uintptr_t(nullptr); // error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'uintptr_t'

ยังจากมาตรฐานฉบับร่างเดียวกัน:

5.2.3 การแปลงประเภทอย่างชัดเจน (สัญกรณ์การทำงาน)
(1) ตัวระบุชนิดง่าย (7.1.6.2) หรือตัวระบุประเภทตัวพิมพ์ (14.6) ตามด้วยรายการนิพจน์วงเล็บที่สร้างขึ้นจะสร้างมูลค่าของประเภทที่ระบุให้กับรายการนิพจน์ หากรายการนิพจน์เป็นนิพจน์เดียวนิพจน์การแปลงประเภทจะเทียบเท่า (ตามที่กำหนดและหากกำหนดไว้ในความหมาย) กับนิพจน์การโยนที่สอดคล้องกัน (5.4) ...

"นิพจน์การโยนที่สอดคล้องกัน (5.4)" สามารถอ้างถึงการส่งแบบ C


0

ทั้งหมดเป็นไปตามมาตรฐาน (อ้างอิงฉบับร่าง n4659 สำหรับ C ++)

nullptr ถูกกำหนดใน [lex.nullptr] เป็น:

ตัวอักษรตัวชี้เป็นคำหลัก nullptr เป็น prvalue ของ type std :: nullptr_t [หมายเหตุ: ... ค่า prvalue ประเภทนี้เป็นค่าคงที่ตัวชี้โมฆะและสามารถแปลงเป็นค่าตัวชี้โมฆะหรือค่าตัวชี้สมาชิกโมฆะ]

แม้ว่าจะไม่บันทึกกฎเกณฑ์หนึ่งนี้ทำให้เห็นได้ชัดว่าสำหรับมาตรฐานnullptrที่คาดว่าจะถูกแปลงเป็นโมฆะชี้คุ้มค่า

เราพบในภายหลัง [conv.ptr]:

ค่าคงที่ตัวชี้ null เป็นตัวอักษรจำนวนเต็มที่มีค่าเป็นศูนย์หรือ prvalue ของประเภท std :: nullptr_t ค่าคงที่ตัวชี้โมฆะสามารถแปลงเป็นประเภทตัวชี้; .... ค่าคงที่ตัวชี้โมฆะของชนิดอินทิกรัลสามารถแปลงเป็น prvalue ของประเภท std :: nullptr_t

ที่นี่อีกครั้งสิ่งที่จำเป็นโดยมาตรฐานคือ0สามารถแปลงเป็น a std::nullptr_tและnullptrสามารถแปลงเป็นตัวชี้ประเภทใดก็ได้

การอ่านของฉันคือมาตรฐานนั้นไม่มีข้อกำหนดว่าnullptrจะสามารถแปลงเป็นรูปแบบอินทิกรัลได้โดยตรงหรือไม่ จากจุดนั้นเมื่อ:

  • MSVC มีการอ่านที่เข้มงวดและห้ามการแปลง
  • เสียงดังกราวและ gcc ทำงานราวกับว่ามีvoid *การแปลงตัวกลาง

1
ฉันคิดว่ามันผิด บางคนคงชี้โมฆะเป็นจำนวนเต็มตัวอักษรที่มีค่าเป็นศูนย์ แต่ไม่ได้เป็นเพราะมีไม่ใช่ประเภทหนึ่งnullptr std::nullptr_t0 สามารถแปลงเป็นstd::nullptr_tค่า nullptrแต่ไม่ให้ที่แท้จริง นี่คือเจตนาทั้งหมดstd::nullptr_tเป็นประเภทที่ จำกัด มากขึ้นเพื่อป้องกันการแปลงที่ไม่ตั้งใจ
MSalters

@MSalters: ฉันคิดว่าคุณพูดถูก ฉันต้องการที่จะอ้างสิทธิ์อีกครั้งและทำผิด ฉันได้แก้ไขโพสต์ด้วยความคิดเห็นของคุณ ขอขอบคุณสำหรับความช่วยเหลือของคุณ.
Serge Ballesta
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.