Statik sınıf üyesine tanımlanmamış başvuru


201

Birisi aşağıdaki kodun neden derlenmeyeceğini açıklayabilir mi? En azından g ++ 4.2.4'te.

Daha da ilginç olan, ÜYE'yi int'e attığımda neden derlenecek?

#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> yerine kodu dört boşluk girintilemek için soruyu düzenledim. Bu, köşeli parantezlerin HTML olarak yorumlanmadığı anlamına gelir.
Steve Jessop

Yanıtlar:


199

Statik üyeyi bir yerde tanımlamanız gerekir (sınıf tanımından sonra). Bunu dene:

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

const int Foo::MEMBER;

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

Bu tanımsız referanstan kurtulmalıdır.


3
İyi nokta, satır içi statik sabit tamsayı başlatma, adresini alamayacağınız kapsamlı bir tamsayı sabiti oluşturur ve vektör bir referans parametresi alır.
Evan Teran

10
Bu cevap sadece sorunun ilk kısmını ele almaktadır. İkinci bölüm çok daha ilginç: NOP dökümünü eklemek neden dış bildirime gerek duymadan çalışmasını sağlıyor?
Brent Bradburn

32
Ben sadece sınıf tanımı bir başlık dosyasında ise, o zaman statik değişken tahsisi başlık değil, uygulama dosyasında olması gerektiğini anlamaya biraz zaman geçirdim.
shanet

1
@shanet: Çok iyi bir nokta - Cevabımda bundan bahsetmeliydim!
Drew Hall

Ama bunu const olarak ilan edersem, bu değişkenin değerini değiştirmem mümkün değil mi?
Namratha

78

Sorun, yeni C ++ özelliklerinin ilginç bir çatışması ve ne yapmaya çalıştığınız nedeniyle ortaya çıkıyor. İlk olarak, push_backimzayı inceleyelim:

void push_back(const T&)

Tür nesnesine bir başvuru bekliyor T. Eski başlatma sistemi altında, böyle bir üye vardır. Örneğin, aşağıdaki kod iyi derlenir:

#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;
}

Bunun nedeni, içinde değeri olan bir yerde gerçek bir nesne olmasıdır. Bununla birlikte, yukarıdaki gibi statik statik üyelerin belirtilmesi için yeni yönteme geçerseniz, Foo::MEMBERartık bir nesne değildir. Sürekli, biraz benzer:

#define MEMBER 1

Ancak bir önişlemci makrosunun baş ağrısı olmadan (ve tip güvenliği ile). Bu, referans bekleyen vektörün bir tane alamadığı anlamına gelir.


2
teşekkürler, bu yardımcı oldu ... bu zaten yoksa stackoverflow.com/questions/1995113/strangest-language-feature için uygun olabilir ...
Andre Holzner 13

1
Ayrıca, MSVC'nin yayın yapılmayan sürümü şikayetsiz olarak kabul ettiğini belirtmek gerekir.
porges

4
-1: Bu doğru değil. Hâlâ bir yerde kullanıldıklarında , satır içi başlatılan statik üyeleri tanımlamanız gerekiyor . Bu derleyici optimizasyon linker hata kurtulmak olabilir bunu değiştirmez. Bu durumda, lvalue-rvalue dönüşümünüz ( (int)döküm sayesinde ), sabitin mükemmel görünürlüğüne sahip çeviri biriminde gerçekleşir ve Foo::MEMBERartık odr kullanılmaz . Bu, referansın başka bir yere aktarıldığı ve başka bir yerde değerlendirildiği ilk fonksiyon çağrısının aksine.
Yörüngedeki Hafiflik Yarışları

Ne olmuş void push_back( const T& value );? const&değerleri ile bağlanabilir.
Kostas

59

Tanım bir şekilde gerekiyorsa, C ++ standardı statik sabit üyeniz için bir tanım gerektirir.

Tanım, örneğin adres kullanılıyorsa gereklidir. push_backparametresini const referansı ile alır ve bu nedenle derleyici üyenizin adresine ihtiyaç duyar ve ad alanında tanımlamanız gerekir.

Sabiti açıkça belirlediğinizde, bir geçici oluşturursunuz ve referansa bağlı olan bu geçicidir (standarttaki özel kurallar altında).

Bu gerçekten ilginç bir durum ve bence sürekli üye için aynı davranışa sahip olacak şekilde std değiştirilecek bir sorun yaratmaya değer!

Her ne kadar garip bir şekilde bu, tekli '+' operatörünün meşru bir kullanımı olarak görülebilir. Temelde bunun sonucu unary +bir değerdir ve bu nedenle değerlerin sabit referanslara bağlanması için kurallar geçerlidir ve statik sabit üyemizin adresini kullanmayız:

v.push_back( +Foo::MEMBER );

3
+1. Evet, T tipi x nesnesi için "(T) x" ifadesinin sabit "x" ifadesini bağlamak için bir const ref'yi bağlamak için kullanılabilmesi kesinlikle gariptir. "Tekli +" hakkındaki gözleminizi seviyorum! Kim zavallı küçük "tek +" aslında bir faydası olduğunu
düşünürdü

3
Genel durum hakkında düşünmek ... C ++ 'da (1) yalnızca tanımlanmışsa lvalue olarak kullanılabileceği ancak (2) olmadan bir değere dönüştürülebildiği başka bir nesne türü var mı? tanımlanmış?
j_random_hacker

Güzel soru, ve en azından şu anda başka örnekler düşünemiyorum. Bu muhtemelen sadece buradadır çünkü komite çoğunlukla mevcut sözdizimini tekrar kullanıyordu.
Richard Corden

@RichardCorden: tekli + bunu nasıl çözdü?
Blood-HaZaRd

1
@ Blood-HaZaRd: Rvalue referanslarından önce sadece aşırı yük push_backa idi const &. Üyenin doğrudan kullanılması, üyenin bir adrese sahip olmasını gerektiren referansa bağlanmasına neden oldu. Ancak, ekleyerek +üye değeri ile geçici oluşturur. Referans daha sonra üyenin bir adrese sahip olmasını gerektirmektense geçici olana bağlanır.
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

Oyuncunun neden çalıştığı hakkında hiçbir fikrim yok, ancak Foo :: MEMBER ilk kez Foo yüklenene kadar tahsis edilmedi ve asla yüklemediğiniz için asla tahsis edilmedi. Bir yerde bir Foo'ya referansınız olsaydı, muhtemelen işe yarardı.


Sanırım kendi sorunuzu cevaplıyorsunuz: Kadro çalışıyor çünkü (geçici) bir referans oluşturuyor.
Jaap Versteegh

1

C ++ 11 ile, yukarıdaki temel türler için mümkün olacaktır:

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

constexprBölüm statik oluşturur ifadesini statik aksine değişken sadece son derece basit bir satır içi yöntemi tanımı gibi bu davranır ve -. Ancak yaklaşım, şablon sınıflarındaki C-string bağlamları ile biraz titrek olduğunu kanıtladı.


Bunun benim için önemli olduğu ortaya çıktı, çünkü "statik sabit int MEMBER = 1;" MEMBER'i ​​anahtarlarda kullanmak gerekirken, dış bildirim vektörlerde kullanmak için gereklidir ve her ikisine de aynı anda sahip olamazsınız. Ama burada vermek ifadesi yapar benim derleyici ile en azından ikisi için çalışmalarını.
Ben Farmer

0

İkinci soru ile ilgili olarak: push_ref parametre olarak referans alır ve bir sınıfın / yapının statik const memeber referansına sahip olamazsınız. Static_cast öğesini çağırdıktan sonra geçici bir değişken oluşturulur. Ve bu nesneye bir referans iletilebilir, her şey iyi çalışır.

Ya da en azından bunu çözen meslektaşım bunu söyledi.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.