ค่าเริ่มต้นของพารามิเตอร์ฟังก์ชัน


130

1

int Add (int a, int b = 3);
int Add (int a, int b)
{

}

2

int Add (int a, int b);
int Add (int a, int b = 3)
{

}

ทั้งสองงาน; วิธีใดเป็นมาตรฐานและเพราะเหตุใด

คำตอบ:


203

หากคุณใส่การประกาศในไฟล์ส่วนหัวและคำจำกัดความใน.cppไฟล์แยกต่างหากและ#includeส่วนหัวจาก.cppไฟล์อื่นคุณจะสามารถเห็นความแตกต่างได้

โดยเฉพาะสมมติว่า:

lib.h

int Add(int a, int b);

lib.cpp

int Add(int a, int b = 3) {
   ...
}

test.cpp

#include "lib.h"

int main() {
    Add(4);
}

การคอมไพล์test.cppจะไม่เห็นการประกาศพารามิเตอร์เริ่มต้นและจะล้มเหลวด้วยข้อผิดพลาด

ด้วยเหตุนี้นิยามพารามิเตอร์ดีฟอลต์มักจะระบุไว้ในการประกาศฟังก์ชัน:

lib.h

int Add(int a, int b = 3);

จากนั้นbจะกำหนดหลาย ๆ ครั้งครั้งเดียวสำหรับแต่ละหน่วยคอมไพล์ที่มีlib.hใช่หรือไม่?
httpinterpret

@httpinterpret: ในแง่หนึ่งใช่ค่าเริ่มต้นของbถูกกำหนดหนึ่งครั้งสำหรับแต่ละ .cppไฟล์ที่มีส่วนหัว แต่ไม่เป็นไรเพราะคุณมีการประกาศAddฟังก์ชันเพียงครั้งเดียว
Greg Hewgill

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

1
คำตอบนี้สามารถแก้ไขได้เนื่องจากการแยกวิเคราะห์อย่างรวดเร็ว (เพียงแค่ดูโค้ดและไม่ดำเนินการจนกว่า "ด้วยเหตุนี้") ทำให้ฉันเข้าใจสิ่งที่ตรงกันข้ามกับที่คุณหมายถึง
Gabriel Devillers

44

ใน C ++ ข้อกำหนดที่กำหนดไว้สำหรับอาร์กิวเมนต์เริ่มต้นที่เกี่ยวข้องกับตำแหน่งของพวกเขาในรายการพารามิเตอร์มีดังนี้:

  1. ต้องระบุอาร์กิวเมนต์เริ่มต้นสำหรับพารามิเตอร์ที่กำหนดไม่เกินหนึ่งครั้ง การระบุมากกว่าหนึ่งครั้ง (แม้จะมีค่าดีฟอลต์เหมือนกัน) ถือเป็นเรื่องผิดกฎหมาย

  2. พารามิเตอร์ที่มีอาร์กิวเมนต์ดีฟอลต์ต้องสร้างกลุ่มที่อยู่ติดกันที่ส่วนท้ายของรายการพารามิเตอร์

ตอนนี้โปรดจำไว้ว่าใน C ++ คุณได้รับอนุญาตให้ "ขยาย" ชุดของพารามิเตอร์ที่มีอาร์กิวเมนต์เริ่มต้นจากการประกาศฟังก์ชันหนึ่งไปยังฟังก์ชันถัดไปตราบเท่าที่ข้อกำหนดข้างต้นยังคงเป็นที่พอใจอย่างต่อเนื่อง

ตัวอย่างเช่นคุณสามารถประกาศฟังก์ชันโดยไม่มีอาร์กิวเมนต์เริ่มต้น

void foo(int a, int b);

ในการเรียกใช้ฟังก์ชันนั้นหลังจากการประกาศดังกล่าวคุณจะต้องระบุอาร์กิวเมนต์ทั้งสองอย่างชัดเจน

ในภายหลัง (ต่อไป) ในหน่วยการแปลเดียวกันคุณสามารถประกาศซ้ำได้อีกครั้ง แต่คราวนี้มีอาร์กิวเมนต์เริ่มต้นเดียว

void foo(int a, int b = 5);

และจากจุดนี้คุณสามารถเรียกมันด้วยอาร์กิวเมนต์ที่ชัดเจนเพียงข้อเดียว

ยิ่งไปกว่านั้นคุณสามารถประกาศอีกครั้งและเพิ่มอาร์กิวเมนต์เริ่มต้นอีกหนึ่งอาร์กิวเมนต์

void foo(int a = 1, int b);

และจากจุดนี้คุณสามารถเรียกมันโดยไม่มีข้อโต้แย้งที่ชัดเจน

ตัวอย่างเต็มอาจมีลักษณะดังนี้

void foo(int a, int b);

int main()
{
  foo(2, 3);

  void foo(int a, int b = 5); // redeclare
  foo(8); // OK, calls `foo(8, 5)`

  void foo(int a = 1, int b); // redeclare again
  foo(); // OK, calls `foo(1, 5)`
}

void foo(int a, int b)
{
  // ...
}

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

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


ฉันไม่คิดว่าโค้ดของคุณที่กำหนด void foo (int a = 1, int b) จะใช้ได้ คุณต้องมีพารามิเตอร์ทางเลือกทั้งหมดหลังจากพารามิเตอร์ทางเลือกหนึ่งตัว มันเป็นข้อผิดพลาดทางไวยากรณ์ (อย่างน้อยกับ g ++ 4.5.3 ในระบบของฉัน)
Nilesh

@ Nilesh: ดังที่ฉันได้กล่าวไว้ข้างต้นอย่างชัดเจน (และซึ่งเป็นจุดรวมของตัวอย่างนี้) เพื่อvoid foo(int a = 1, int b)ให้ทำงานได้จะต้องมีการประกาศหลังจาก void foo(int a, int b = 5)นั้น ใช่มันจะทำงาน และไม่ใช่ไม่ใช่ข้อผิดพลาดทางไวยากรณ์ g ++ 4.5.3 จะคอมไพล์ได้อย่างดีเยี่ยม
AnT

โอเคดังนั้นฟังก์ชันจึงรับค่า b จากการประกาศครั้งก่อน รับสิ่งที่ตอนนี้ ขอบคุณ :-)
Nilesh

1
@Nilesh: ใช่การประกาศอาร์กิวเมนต์เริ่มต้นจะถูกสะสมในการประกาศก่อนหน้าทั้งหมดในหน่วยการแปล
AnT

1
ฉันชอบเขียนต้นแบบฟังก์ชันของฉันโดยไม่มีชื่อตัวแปรเช่นint foo(int). ฉันพบว่าฉันสามารถเขียนได้int foo(int=5)อีกครั้งโดยเว้นชื่อพารามิเตอร์ไว้ ดูเหมือนจะยังไม่มีใครพูดถึงเรื่องนี้
Victor Eijkhout

5

วิธีแรกจะเป็นวิธีที่สอง

เนื่องจากไฟล์ส่วนหัวจะแสดงว่าพารามิเตอร์เป็นทางเลือกและค่าเริ่มต้นจะเป็นเท่าใด นอกจากนี้สิ่งนี้จะช่วยให้มั่นใจได้ว่าค่าเริ่มต้นจะเหมือนกันไม่ว่าจะใช้ไฟล์. cpp ที่เกี่ยวข้องก็ตาม

ในวิธีที่สองไม่มีการรับประกันค่าเริ่มต้นสำหรับพารามิเตอร์ที่สอง ค่าเริ่มต้นอาจเปลี่ยนแปลงได้ขึ้นอยู่กับวิธีการใช้งานไฟล์. cpp ที่เกี่ยวข้อง


4

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

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