เหตุใดจึงส่งคืนค่าที่ไม่ได้ใช้ให้เป็นโมฆะ


112
int fn();

void whatever()
{
    (void) fn();
}

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

ติดตาม:

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

ฉันจะกินคำพูดของฉันถ้าแมลงหนีเพราะมัน ...

คำตอบ:


79

คำตอบของ David ครอบคลุมถึงแรงจูงใจในการทำเช่นนี้อย่างชัดเจนเพื่อแสดงให้ "นักพัฒนา" คนอื่น ๆ ทราบอย่างชัดเจนว่าคุณทราบว่าฟังก์ชันนี้ส่งกลับมา แต่คุณเพิกเฉยอย่างชัดเจน

นี่เป็นวิธีตรวจสอบให้แน่ใจว่ามีการจัดการรหัสข้อผิดพลาดที่จำเป็นอยู่เสมอ

ฉันคิดว่าสำหรับ C ++ นี่อาจเป็นที่เดียวที่ฉันชอบใช้แคสต์สไตล์ C เช่นกันเนื่องจากการใช้สัญกรณ์การร่ายแบบคงที่เต็มไปด้วยความรู้สึกเหมือน overkill ที่นี่ สุดท้ายหากคุณกำลังตรวจสอบมาตรฐานการเข้ารหัสหรือเขียนมาตรฐานคุณควรระบุอย่างชัดเจนว่าการเรียกใช้ตัวดำเนินการที่โอเวอร์โหลด (ไม่ใช้สัญกรณ์เรียกฟังก์ชัน) ควรได้รับการยกเว้นจากสิ่งนี้ด้วยเช่นกัน:

class A {};
A operator+(A const &, A const &);

int main () {
  A a;
  a + a;                 // Not a problem
  (void)operator+(a,a);  // Using function call notation - so add the cast.

นี่เป็นที่เดียวที่ฉันชอบนักแสดงสไตล์ c ด้วย ในมาตรฐานการเข้ารหัสของฉันฉันจะเพิ่ม (โมฆะ) เป็น "a + a;" ด้วย (แน่นอนว่าถูกจัดวางอย่างถูกต้อง: p)
Johannes Schaub - litb

8
ค่อนข้างไม่ตรงประเด็น แต่ทำไมคุณถึงทำ "a + a;" แบบนั้นโดยไม่ใช้ค่าส่งคืน? นั่นดูเหมือนเป็นการใช้ผลข้างเคียงในทางที่ผิดสำหรับฉันและทำให้เจตนาของรหัสนั้นสับสน
Rob K

5
สิ่งที่คุณจะใช้ทุกวันคือ 'a = a' สิ่งนี้ส่งคืนค่าที่กำหนดให้กับอ็อบเจ็กต์ (สำหรับคลาสที่มีพฤติกรรมดีทั้งหมดนั่นคือ! :)) อีกตัวอย่างหนึ่งคือ os << "Hello World" << std :: endl. แต่ละรายการส่งคืนอ็อบเจ็กต์ "os"
Richard Corden

46

ในที่ทำงานเราใช้สิ่งนั้นเพื่อรับทราบว่าฟังก์ชันมีค่าตอบแทน แต่ผู้พัฒนายืนยันว่าปลอดภัยที่จะเพิกเฉย เนื่องจากคุณติดแท็กคำถามเป็น C ++ คุณควรใช้static_cast :

static_cast<void>(fn());

เท่าที่คอมไพเลอร์ส่งค่ากลับเป็นโมฆะมีความหมายเพียงเล็กน้อย


มันระงับคำเตือนเกี่ยวกับค่าส่งคืนที่ไม่ได้ใช้หรือไม่?
Paul Tomblin

ไม่มันไม่ได้ @Mykola: ด้วย GCC4 คุณสามารถแนบแอตทริบิวต์ที่ระบุว่าไม่ควรละเว้นค่าส่งคืนซึ่งจะทำให้เกิดการเตือน
David Holm

2
ฉันใช้ g ++ และมันให้คำเตือนแม้จะมีการร่ายแบบนี้ก็ตาม
klew

2
คุณใช้ตัวเลือกคำเตือนใด -Wunused-value ไม่ทำให้เกิดการเตือนในสภาพแวดล้อมของฉัน
Mykola Golubyev

ใน VC ++ ยับยั้งคำเตือน
jalf

39

เหตุผลที่แท้จริงในการทำเช่นนี้ย้อนหลังไปถึงเครื่องมือที่ใช้กับรหัส C ที่เรียกว่าlintผ้าสำลี

จะวิเคราะห์โค้ดเพื่อค้นหาปัญหาที่เป็นไปได้และออกคำเตือนและคำแนะนำ หากฟังก์ชันส่งคืนค่าที่ไม่ได้ตรวจสอบlintจะเตือนในกรณีที่เกิดอุบัติเหตุ หากต้องการความเงียบในการเตือนนี้คุณโยนการเรียกร้องให้lint(void)


2
อาจเริ่มต้นด้วยวิธีนี้ แต่ปัจจุบันเครื่องมือส่วนใหญ่มีกลไกอื่นในการระงับคำเตือนเช่นนี้ นอกจากนี้ - ไม่ว่าเหตุใดสิ่งนี้จึงเริ่มต้นขึ้นภายในขอบเขตของรหัสสำคัญด้านความปลอดภัยโดยเฉพาะสิ่งนี้กลายเป็นวิธีปกติในการ "จัดทำเอกสาร" เจตนาของนักพัฒนา
Richard Corden

5
GCC ให้คำเตือนสำหรับสิ่งนี้ด้วย -Wall
greyfade

23

Casting to voidใช้เพื่อระงับคำเตือนของคอมไพเลอร์สำหรับตัวแปรที่ไม่ได้ใช้และค่าหรือนิพจน์ที่ส่งคืนที่ไม่ได้บันทึก

มาตรฐาน (2003) กล่าวใน§5.2.9 / 4 กล่าวว่า

นิพจน์ใด ๆ สามารถแปลงเป็นประเภท "cv void" ได้อย่างชัดเจน มูลค่าการแสดงออกถูกทิ้ง

ดังนั้นคุณสามารถเขียน:

//suppressing unused variable warnings
static_cast<void>(unusedVar);
static_cast<const void>(unusedVar);
static_cast<volatile void>(unusedVar);

//suppressing return value warnings
static_cast<void>(fn());
static_cast<const void>(fn());
static_cast<volatile void>(fn());

//suppressing unsaved expressions
static_cast<void>(a + b * 10);
static_cast<const void>( x &&y || z);
static_cast<volatile void>( m | n + fn());

ทุกรูปแบบถูกต้อง ฉันมักจะทำให้สั้นลงเป็น:

//suppressing  expressions
(void)(unusedVar);
(void)(fn());
(void)(x &&y || z);

มันก็โอเค


1
ยกเว้นว่าจะไม่ปิดคำเตือนของคอมไพเลอร์เสมอไป gcc ดูเหมือนจะไม่ตอบสนองต่อการคัดเลือกให้เป็นโมฆะ
Matt

@ แมท: คุณใช้ GCC เวอร์ชั่นไหน? คุณสามารถโพสต์รหัสของคุณที่ ideone.com หรือ stacked-crooked.com (อันหลังดีกว่า)
Nawaz

1
คำว่า "ทิ้ง" มาตรฐานหมายความว่าคำเตือนไม่ควรขึ้นด้วยเศษขยะหรือไม่?
Ciro Santilli 郝海东冠状病六四事件法轮功

2
@CiroSantilli 巴拿馬文件六四事件法轮功: .... นั่นเป็นคำถามที่น่าสนใจ ขณะนี้ฉันไม่รู้คำตอบ โปรดแบ่งปันกับฉันหากคุณได้รับรู้
Nawaz

8

เนื่องจาก c ++ 17 เรามี[[maybe_unused]]แอตทริบิวต์ที่สามารถใช้แทนการแคvoidสต์ได้


1
นอกจากนี้ C ++ 17 ยังเพิ่มnodiscardซึ่งอาจเป็นที่สนใจ: stackoverflow.com/a/61675726/895245นอกจากนี้ฉันคิดว่าคุณไม่สามารถใช้[[maybe_unused]]กับการเรียกได้โดยตรงกับตัวแปรดัมมี่ที่ยอมรับการโทรกลับเท่านั้น? ถ้าถูกต้องมันก็วอกเล็กน้อย
Ciro Santilli 郝海东冠状病六四事件法轮功

4

Cast to void นั้นไม่มีค่าใช้จ่าย เป็นเพียงข้อมูลสำหรับคอมไพเลอร์วิธีการรักษาเท่านั้น


1
มัน "ไม่มีค่าใช้จ่าย" ถ้าเวลาในการเขียนและการอ่านครั้งต่อ ๆ ไปการทำความเข้าใจ (แล้วเพิกเฉย) รหัสนั้นฟรี IMO การเขียน(void)ทำให้ฉันเสียเวลาและพลังงานและทำให้ฉันสงสัยว่าผู้เขียนรู้หรือไม่ว่านิพจน์ทำงานอย่างไร
wallyk

4

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


2
นี่เป็นรูปแบบที่ทราบกันดีในการบอกคนอื่นอย่างชัดเจนว่าคุณไม่สนใจเกี่ยวกับมูลค่าที่ส่งคืนซึ่งคุณไม่ได้ลืมที่จะจัดการกับมัน
Spidey

For the functionality of you program casting to void is meaninglessสำหรับการจัดทำเอกสารด้วยตนเองและจำนวนคำเตือนเมื่อรวบรวมนั้นไม่ได้จริงๆ you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.ความคิดเห็นที่ดีที่สุดคือไม่มีความคิดเห็นเมื่อรหัสอธิบายตัวเอง และอีกครั้งให้คอมไพเลอร์คำเตือนที่ถูกต้องมีความสุขในกระบวนการ
underscore_d

'การเพิ่มนักแสดงเช่นนี้จะดูแปลกและทำให้เกิดคำถามเกี่ยวกับเหตุผลที่เป็นไปได้' ฉันค้นพบหนึ่งในสิ่งเหล่านี้และนั่นคือเหตุผลที่ฉันอ่านสิ่งนี้ ดูเหมือนเป็นการทำร้ายสมองที่ไม่ได้รับการฝึกฝนอย่างลึกลับ
mynameisnafe

1

นอกจากนี้เมื่อตรวจสอบว่ารหัสของคุณเป็นไปตามมาตรฐาน MISTA (หรืออื่น ๆ ) เครื่องมืออัตโนมัติเช่น LDRA จะไม่อนุญาตให้คุณเรียกใช้ฟังก์ชันที่มีประเภทการส่งคืนโดยไม่ต้องส่งคืนค่าเว้นแต่คุณจะโยนค่าที่ส่งคืนไปยัง (โมฆะ) อย่างชัดเจน


0

C ++ 17 [[nodiscard]]

C ++ 17 สร้างมาตรฐาน "ธุรกิจที่ถูกละเว้น" ด้วยแอตทริบิวต์

ดังนั้นฉันหวังว่าการใช้งานที่เป็นไปตามข้อกำหนดจะเตือนเมื่อ nodiscardมีการให้เท่านั้นและจะไม่เตือนเป็นอย่างอื่น

ตัวอย่าง:

main.cpp

[[nodiscard]] int f() {
    return 1;
}

int main() {
    f();
}

รวบรวม:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -o main.out main.cpp

ผล:

main.cpp: In function int main()’:
main.cpp:6:6: warning: ignoring return value of int f()’, declared with attribute nodiscard [-Wunused-result]
    6 |     f();
      |     ~^~
main.cpp:1:19: note: declared here
    1 | [[nodiscard]] int f() {
      | 

สิ่งต่อไปนี้หลีกเลี่ยงคำเตือน:

(void)f();
[[maybe_unused]] int i = f();

ฉันไม่สามารถใช้งานmaybe_unusedได้โดยตรงในการf()โทร:

[[maybe_unused]] f();

ให้:

main.cpp: In function int main()’:
main.cpp:6:5: warning: attributes at the beginning of statement are ignored [-Wattributes]
    6 |     [[maybe_unused]] f();
      |     ^~~~~~~~~~~~~~~~

การ(void)ทำงานของนักแสดงดูเหมือนจะไม่บังคับ แต่ได้รับการ "สนับสนุน" ในมาตรฐาน: ฉันจะทิ้งค่าส่งคืน [[nodiscard]] โดยเจตนาได้อย่างไร

ตามที่เห็นในข้อความเตือน "วิธีแก้ไข" อย่างหนึ่งของคำเตือนคือการเพิ่ม-Wno-unused-result:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -Wno-unused-result -o main.out main.cpp

แม้ว่าฉันจะไม่แนะนำให้เพิกเฉยต่อคำเตือนทั่วโลกเช่นนี้

C ++ 20 ยังช่วยให้คุณสามารถเพิ่มเหตุผลnodiscardตาม[[nodiscard("reason")]]ที่ระบุไว้ใน: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

GCC warn_unused_resultแอตทริบิวต์

ก่อนการกำหนดมาตรฐาน[[nodiscard]]และสำหรับ C ก่อนที่พวกเขาจะตัดสินใจกำหนดมาตรฐานแอตทริบิวต์ในที่สุด GCC ได้ใช้ฟังก์ชันเดียวกันกับwarn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

ซึ่งจะช่วยให้:

main.cpp: In function int main()’:
main.cpp:8:6: warning: ignoring return value of int f()’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     f();
      |     ~^~

ควรสังเกตว่าเนื่องจาก ANSI C ไม่มีมาตรฐานสำหรับสิ่งนี้ ANSI C จึงไม่ได้ระบุว่าฟังก์ชันไลบรารีมาตรฐาน C ใดมีแอตทริบิวต์หรือไม่ดังนั้นการใช้งานจึงได้ทำการตัดสินใจด้วยตนเองว่าควรทำเครื่องหมายอะไรหรือไม่warn_unuesd_resultซึ่ง เป็นเหตุผลว่าทำไมโดยทั่วไปแล้วคุณจะต้องใช้การแค(void)สต์เพื่อละเว้นผลตอบแทนของการเรียกใช้ฟังก์ชันไลบรารีมาตรฐานเพื่อหลีกเลี่ยงคำเตือนในการใช้งานใด ๆ

ทดสอบใน GCC 9.2.1, Ubuntu 19.10

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