การอ้างอิงที่ไม่ได้กำหนดสำหรับสมาชิกคลาสแบบสแตติก


201

ใครสามารถอธิบายได้ว่าทำไมรหัสต่อไปนี้จะไม่รวบรวม อย่างน้อยใน g ++ 4.2.4

และน่าสนใจมากขึ้นทำไมมันจะรวบรวมเมื่อฉันเลือกสมาชิกเพื่อ int?

#include <vector>

class Foo {  
public:  
    static const int MEMBER = 1;  
};

int main(){  
    vector<int> v;  
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

ฉันแก้ไขคำถามเพื่อเยื้องรหัสด้วยช่องว่างสี่ช่องแทนที่จะใช้ <pre> <code> </code> </pre> ซึ่งหมายความว่าเครื่องหมายวงเล็บมุมจะไม่ถูกตีความเป็น HTML
Steve Jessop

stackoverflow.com/questions/16284629/…คุณสามารถอ้างถึงคำถามนี้
Tooba Iqbal

คำตอบ:


199

คุณต้องกำหนดสมาชิกแบบสแตติกที่ใดที่หนึ่ง (หลังจากคำจำกัดความของคลาส) ลองสิ่งนี้:

class Foo { /* ... */ };

const int Foo::MEMBER;

int main() { /* ... */ }

ที่ควรกำจัดของการอ้างอิงที่ไม่ได้กำหนด


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

10
คำตอบนี้กล่าวถึงส่วนแรกของคำถามเท่านั้น ส่วนที่สองน่าสนใจยิ่งขึ้น: เหตุใดการเพิ่มนักแสดง NOP จึงสามารถทำงานได้โดยไม่ต้องมีการประกาศจากภายนอก
nobar

32
ฉันใช้เวลาพอสมควรในการหาว่าถ้านิยามคลาสอยู่ในไฟล์ส่วนหัวการจัดสรรตัวแปรแบบสแตติกควรอยู่ในไฟล์การนำไปใช้ไม่ใช่ส่วนหัว
shanet

1
@shanet: จุดที่ดีมาก - ฉันควรจะกล่าวถึงในคำตอบของฉัน!
Drew Hall

แต่ถ้าฉันประกาศว่ามันเป็นค่าคงที่ฉันไม่สามารถเปลี่ยนค่าของตัวแปรนั้นได้หรือไม่?
Namratha

78

ปัญหาเกิดขึ้นเนื่องจากมีการปะทะกันของคุณสมบัติ C ++ ใหม่และสิ่งที่คุณพยายามทำ ก่อนอื่นเรามาดูpush_backลายเซ็น:

void push_back(const T&)

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

#include <vector>

class Foo {
public:
    static const int MEMBER;
};

const int Foo::MEMBER = 1; 

int main(){
    std::vector<int> v;
    v.push_back( Foo::MEMBER );       // undefined reference to `Foo::MEMBER'
    v.push_back( (int) Foo::MEMBER ); // OK  
    return 0;
}

เนื่องจากมีวัตถุจริงอยู่ที่ไหนสักแห่งที่มีค่านั้นเก็บอยู่ในนั้น อย่างไรก็ตามหากคุณเปลี่ยนไปใช้วิธีการใหม่ในการระบุสมาชิก const แบบคงที่เช่นที่คุณมีด้านบนFoo::MEMBERจะไม่เป็นวัตถุอีกต่อไป เป็นค่าคงที่ค่อนข้างคล้ายกับ:

#define MEMBER 1

แต่ไม่มีอาการปวดหัวของมาโครตัวประมวลผลล่วงหน้า (และด้วยความปลอดภัยของประเภท) นั่นหมายความว่าเวกเตอร์ที่ต้องการการอ้างอิงไม่สามารถหาได้


2
ขอบคุณที่ช่วย ... ที่สามารถมีสิทธิ์ได้รับstackoverflow.com/questions/1995113/strangest-language-featureหากยังไม่มี ...
Andre Holzner

1
นอกจากนี้ยังควรสังเกตว่า MSVC ยอมรับรุ่นที่ไม่ส่งโดยไม่มีการร้องเรียน
porges

4
-1: นี่ไม่เป็นความจริงเลย คุณควรจะยังคงกำหนดสมาชิกแบบคงที่เริ่มต้นแบบอินไลน์เมื่อพวกเขาจะถูกใช้งานที่ไหนสักแห่ง การเพิ่มประสิทธิภาพคอมไพเลอร์นั้นอาจกำจัดข้อผิดพลาด linker ของคุณไม่เปลี่ยนแปลง ในกรณีนี้คุณแปลง lvalue ไป rvalue (ขอบคุณที่(int)หล่อ) เกิดขึ้นในหน่วยการแปลมีการแสดงผลที่สมบูรณ์แบบของอย่างต่อเนื่องและFoo::MEMBERเป็นไปไม่ODR ใช้ สิ่งนี้ตรงกันข้ามกับการเรียกใช้ฟังก์ชันแรกซึ่งการอ้างอิงถูกส่งผ่านและประเมินที่อื่น
การแข่งขัน Lightness ใน Orbit

เกี่ยวกับvoid push_back( const T& value );อะไร const&สามารถผูกกับค่า
Kostas

59

มาตรฐาน C ++ ต้องการนิยามสำหรับสมาชิก const คงที่ของคุณหากจำเป็นต้องใช้นิยามอย่างใด

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

เมื่อคุณสร้างค่าคงที่อย่างชัดเจนคุณกำลังสร้างชั่วคราวและเป็นชั่วคราวซึ่งถูกผูกไว้กับการอ้างอิง (ภายใต้กฎพิเศษในมาตรฐาน)

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

แม้ว่าในทางแปลก ๆ สิ่งนี้อาจถูกมองว่าเป็นการใช้งานที่ถูกต้องตามกฎหมายของผู้ดำเนินการ '+' โดยพื้นฐานแล้วผลลัพธ์ของการunary +เป็นค่า rvalue และดังนั้นกฎสำหรับการผูกค่า rvalues ​​กับการอ้างอิง const ใช้และเราไม่ใช้ที่อยู่ของสมาชิก const คงที่ของเรา:

v.push_back( +Foo::MEMBER );

3
+1 ใช่มันแปลกอย่างแน่นอนสำหรับวัตถุ x ของประเภท T การแสดงออก "(T) x" สามารถใช้ในการผูกอ้างอิง const ในขณะที่ธรรมดา "x" ไม่สามารถ ฉันชอบการสังเกตของคุณเกี่ยวกับ "unary +"! ใครจะคิดว่าไม่ดีเล็ก ๆ น้อย ๆ "เอก +" จริงมีการใช้งาน ... :)
j_random_hacker

3
กำลังคิดเกี่ยวกับกรณีทั่วไป ... มีวัตถุชนิดอื่นใน C ++ ที่มีคุณสมบัติที่ (1) สามารถใช้เป็น lvalue เฉพาะเมื่อมันถูกกำหนด แต่ (2) สามารถแปลงเป็น rvalue โดยไม่ต้องถูก กำหนด?
j_random_hacker

เป็นคำถามที่ดีและอย่างน้อยตอนนี้ฉันก็ไม่สามารถคิดถึงตัวอย่างอื่น ๆ ได้ นี่อาจเป็นเพียงที่นี่เพราะคณะกรรมการส่วนใหญ่เพิ่งใช้ไวยากรณ์ที่มีอยู่ซ้ำ
Richard Corden

@RichardCorden: unary + แก้ไขปัญหาได้อย่างไร
Blood-HaZaRd

1
@ เลือดอันตราย: ก่อนที่จะอ้างอิง rvalue เกินพิกัดเดียวของเป็นpush_back const &การใช้สมาชิกโดยตรงทำให้สมาชิกต้องผูกพันกับข้อมูลอ้างอิงซึ่งจำเป็นต้องมีที่อยู่ อย่างไรก็ตามการเพิ่มการ+สร้างชั่วคราวที่มีค่าของสมาชิก การอ้างอิงนั้นผูกกับชั่วคราวนั้นแทนที่จะต้องการสมาชิกมีที่อยู่
Richard Corden

10

Aaa.h

class Aaa {

protected:

    static Aaa *defaultAaa;

};

Aaa.cpp

// You must define an actual variable in your program for the static members of the classes

static Aaa *Aaa::defaultAaa;

1

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


ฉันคิดว่าคุณกำลังตอบคำถามของคุณเอง: นักแสดงได้ผลเพราะมันสร้างการอ้างอิง (ชั่วคราว)
Jaap Versteegh

1

ด้วย C ++ 11 สิ่งที่กล่าวมาข้างต้นอาจเป็นไปได้สำหรับประเภทพื้นฐานดังนี้

class Foo {
public:  
  static constexpr int MEMBER = 1;  
};

constexprส่วนสร้างแบบคงที่แสดงออกเมื่อเทียบกับคงที่ตัวแปร - และพฤติกรรมที่เช่นเดียวกับความหมายวิธีการแบบอินไลน์ง่ายมาก วิธีการพิสูจน์เล็กน้อยสั่นคลอนด้วย constexprs C- สตริงในชั้นเรียนแม่แบบแม้ว่า


สิ่งนี้กลายเป็นสิ่งจำเป็นสำหรับฉันเพราะ "คง const const int MEMBER = 1;" จำเป็นต้องใช้ MEMBER ในสวิตช์ในขณะที่จำเป็นต้องใช้การประกาศภายนอกเพื่อใช้งานในเวกเตอร์และคุณไม่สามารถมีทั้งสองอย่างในเวลาเดียวกัน แต่การแสดงออกที่คุณให้ที่นี่ไม่ทำงานทั้งสองอย่างน้อยกับคอมไพเลอร์ของฉัน
ชาวนาเบ็น

0

เกี่ยวกับคำถามที่สอง: push_ref รับการอ้างอิงเป็นพารามิเตอร์และคุณไม่สามารถมีการอ้างอิงถึง const const แบบคงที่ของคลาส / โครงสร้าง เมื่อคุณเรียก static_cast ตัวแปรชั่วคราวจะถูกสร้างขึ้น และการอ้างอิงถึงวัตถุนี้สามารถส่งผ่านได้ทุกอย่างทำงานได้ดี

หรืออย่างน้อยเพื่อนร่วมงานของฉันที่แก้ไขปัญหานี้กล่าวเช่นนั้น

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