“ int & foo ()” ใน C ++ หมายความว่าอย่างไร


114

ในขณะที่อ่านคำอธิบายเกี่ยวกับ lvalues ​​และ rvalues ​​บรรทัดของโค้ดเหล่านี้ติดอยู่กับฉัน:

int& foo();
foo() = 42; // OK, foo() is an lvalue

ฉันลองใช้ใน g ++ แต่คอมไพเลอร์แจ้งว่า "undefined reference to foo ()" ถ้าฉันเพิ่ม

int foo()
{
  return 2;
}

int main()
{
  int& foo();
  foo() = 42;
}

มันรวบรวมดี แต่ใช้มันให้ความผิดส่วน เพียงแค่บรรทัด

int& foo();

โดยตัวมันเองทั้งคอมไพล์และรันโดยไม่มีปัญหาใด ๆ

รหัสนี้หมายถึงอะไร? คุณจะกำหนดค่าให้กับการเรียกใช้ฟังก์ชันได้อย่างไรและเหตุใดจึงไม่ใช่ค่า rvalue


25
คุณไม่ได้กำหนดค่าให้กับสายงานที่คุณกำลังกำหนดค่าการอ้างอิงก็จะส่งกลับ
Frédéric Hamidi

3
@ FrédéricHamidiกำหนดค่าให้กับวัตถุที่การอ้างอิงที่ส่งคืนอ้างถึง
MM

3
ใช่มันเป็นนามแฝงสำหรับวัตถุ การอ้างอิงไม่ใช่วัตถุ
MM

2
การประกาศฟังก์ชันท้องถิ่นของ @RoyFalk เป็นโรคฝีโดยส่วนตัวฉันยินดีที่จะเห็นพวกเขาถูกลบออกจากภาษา (ไม่รวม lambdas แน่นอน)
MM

คำตอบ:


168

คำอธิบายคือสมมติว่ามีการดำเนินการบางอย่างที่เหมาะสมสำหรับการfooซึ่งผลตอบแทนอ้างอิง lvalue intการที่ถูกต้อง

การดำเนินการดังกล่าวอาจเป็น:

int a = 2; //global variable, lives until program termination

int& foo() {
    return a;
} 

ตอนนี้เนื่องจากfooส่งคืนการอ้างอิง lvalue เราสามารถกำหนดค่าบางอย่างให้กับค่าส่งคืนได้ดังนี้:

foo() = 42;

สิ่งนี้จะอัปเดต global aด้วยค่า42ซึ่งเราสามารถตรวจสอบได้โดยการเข้าถึงตัวแปรโดยตรงหรือเรียกfooอีกครั้ง:

int main() {
    foo() = 42;
    std::cout << a;     //prints 42
    std::cout << foo(); //also prints 42
}

สมเหตุสมผลในตัวดำเนินการ []? หรือสมาชิกคนอื่น ๆ เข้าถึงวิธีการ?
ม.ค. Dorniak

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

72

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

int& highest(int  & i, int  & j)
{
    if (i > j)
    {
        return i;
    }
    return j;
}

int main()
{
    int a{ 3};
    int b{ 4 };
    highest(a, b) = 11;
    return 0;
}

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


5
มีเหตุผลที่จะเขียน int a {3} แทน int a = 3 หรือไม่? ไวยากรณ์นี้ดูไม่เหมาะสมสำหรับฉัน
Aleksei Petrenko

6
@AlexPetrenko นั่นคือการเริ่มต้นสากล ฉันบังเอิญใช้มันในตัวอย่างที่มีประโยชน์ ไม่มีอะไรที่ไม่เหมาะสมเกี่ยวกับเรื่องนี้
Kate Gregory

3
ฉันรู้ว่านี่คืออะไร a = 3 รู้สึกสะอาดขึ้นมาก ความชอบส่วนตัว)
Aleksei Petrenko

เช่นเดียวกับการประกาศค่าคงที่ภายในฟังก์ชันอาจทำให้บางคนสับสนฉันยังเชื่อว่าการใช้การเริ่มต้นแบบสากลมีแนวโน้มที่จะทำได้
ericcurtin

@AlekseiPetrenko ในกรณีที่ int a = 1.3 จะแปลงเป็น a = 1 โดยปริยายในขณะที่ int a {1.3} ส่งผลให้เกิดข้อผิดพลาด: ทำให้การแปลงแคบลง
Sathvik

32
int& foo();

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

int & foo()
{
    static int bar = 0;
    return bar;
}

barตอนนี้เรามีฟังก์ชั่นที่ส่งกลับอ้างอิงถึง เนื่องจากแถบstaticจะเปิดอยู่หลังจากการเรียกใช้ฟังก์ชันดังนั้นการส่งคืนการอ้างอิงถึงมันจึงปลอดภัย ทีนี้ถ้าเราทำ

foo() = 42;

สิ่งที่เกิดขึ้นคือเรากำหนด 42 เนื่องจากเรากำหนดให้การอ้างอิงและการอ้างอิงเป็นเพียงนามแฝงสำหรับbar barถ้าเราเรียกใช้ฟังก์ชันอีกครั้งเช่น

std::cout << foo();

มันจะพิมพ์ 42 เนื่องจากเราตั้งค่าbarเป็นด้านบน


15

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

int foo()ในความพยายามที่สองของคุณคุณให้ฟังก์ชั่น int& foo();นี้มีชนิดกลับแตกต่างกันเพื่อฟังก์ชั่นการประกาศโดย ดังนั้นคุณจึงมีคำประกาศที่เหมือนกันสองรายการfooที่ไม่ตรงกันซึ่งละเมิดกฎคำจำกัดความเดียวซึ่งทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด (ไม่จำเป็นต้องวินิจฉัย)

สำหรับสิ่งที่ได้ผลให้นำการประกาศฟังก์ชันภายในเครื่องออก สิ่งเหล่านี้สามารถนำไปสู่พฤติกรรมเงียบ ๆ ที่ไม่ได้กำหนดอย่างที่คุณเคยเห็น ให้ใช้เฉพาะการประกาศฟังก์ชันภายนอกฟังก์ชันเท่านั้น โปรแกรมของคุณอาจมีลักษณะดังนี้:

int &foo()
{
    static int i = 2;
    return i;
}  

int main()
{
    ++foo();  
    std::cout << foo() << '\n';
}

10

int& foo();intเป็นฟังก์ชั่นที่กลับมาอ้างอิงถึง ฟังก์ชันที่คุณระบุจะส่งคืนintโดยไม่มีการอ้างอิง

คุณสามารถทำได้

int& foo()
{
    static int i = 42;
    return i;
}

int main()
{
    int& foo();
    foo() = 42;
}

7

int & foo();หมายความว่าfoo()ส่งคืนการอ้างอิงไปยังตัวแปร

พิจารณารหัสนี้:

#include <iostream>
int k = 0;

int &foo()
{
    return k;
}

int main(int argc,char **argv)
{
    k = 4;
    foo() = 5;
    std::cout << "k=" << k << "\n";
    return 0;
}

รหัสนี้พิมพ์:

$ ./a.out k = 5

เพราะผลตอบแทนอ้างอิงกับตัวแปรโลกfoo()k

ในโค้ดที่แก้ไขของคุณคุณกำลังแคสต์ค่าที่ส่งคืนไปยังข้อมูลอ้างอิงซึ่งถือว่าไม่ถูกต้อง


5

ในบริบทนั้น & หมายถึงการอ้างอิง - ดังนั้น foo จึงส่งคืนการอ้างอิงไปยัง int แทนที่จะเป็น int

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

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

หากคุณสงสัย - สาเหตุที่คุณได้รับค่า segfault เป็นเพราะคุณส่งคืนตัวอักษรตัวเลข '2' - ดังนั้นจึงเป็นข้อผิดพลาดที่แน่นอนที่คุณจะได้รับหากคุณกำหนด const int แล้วลองแก้ไข ความคุ้มค่า

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


4

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

ในตัวอย่างของคุณfooแสดงค่า lvalue ตามลายเซ็นอย่างชัดเจน แต่คุณคืนค่า rvalue ที่แปลงเป็น lvalue เห็นได้ชัดว่าตั้งใจจะล้มเหลว คุณสามารถทำได้:

int& foo()
{
    static int x;
    return x;
}

และจะประสบความสำเร็จโดยการเปลี่ยนค่าของ x เมื่อพูดว่า:

foo() = 10;

3

ฟังก์ชันที่คุณมี foo () คือฟังก์ชันที่ส่งกลับการอ้างอิงเป็นจำนวนเต็ม

สมมติว่าเดิม foo ส่งกลับ 5 และต่อมาในฟังก์ชันหลักของคุณคุณพูดfoo() = 10;แล้วพิมพ์ foo ออกมามันจะพิมพ์ 10 แทนที่จะเป็น 5

ฉันหวังว่ามันจะสมเหตุสมผล :)

ฉันยังใหม่กับการเขียนโปรแกรมด้วย มันน่าสนใจที่จะเห็นคำถามเช่นนี้ที่ทำให้คุณคิด! :)

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