Sınıf tanımında statik const tamsayı üyelerini tanımlama


109

Anladığım kadarıyla, C ++, statik const üyelerinin bir tamsayı türü olduğu sürece bir sınıf içinde tanımlanmasına izin veriyor.

Öyleyse neden aşağıdaki kod bana bir bağlayıcı hatası veriyor?

#include <algorithm>
#include <iostream>

class test
{
public:
    static const int N = 10;
};

int main()
{
    std::cout << test::N << "\n";
    std::min(9, test::N);
}

Aldığım hata:

test.cpp:(.text+0x130): undefined reference to `test::N'
collect2: ld returned 1 exit status

İlginç bir şekilde, std :: min çağrısına yorum yaparsam, kod derlenir ve bağlanır (test :: N'ye önceki satırda da başvurulsa bile).

Neler olduğuna dair bir fikriniz var mı?

Derleyicim Linux'ta gcc 4.4.


3
Visual Studio 2010'da sorunsuz çalışıyor.
Puppy


Özel durumda, charbunun yerine olarak tanımlayabilirsiniz constexpr static const char &N = "n"[0];. Not &. Sanırım bu işe yarıyor çünkü değişmez dizeler otomatik olarak tanımlanıyor. Yine de bunun için biraz endişeliyim - dize muhtemelen birden fazla farklı adreste olacağından, farklı çeviri birimleri arasında bir başlık dosyasında garip davranabilir.
Aaron McDaid

1
Bu soru, C ++ "sabitler için # tanımları kullanmayın" yanıtının ne kadar zayıf olduğunun bir göstergesidir.
Johannes Overmann

1
@JohannesOvermann Bu bağlamda, C ++ 17'den beri global değişkenler için inline kullanımından bahsetmek istiyorum, ki bildiğim kadarıyla inline const int N = 10hala bir yerde linker tarafından tanımlanan bir depolama alanı var. Bu durumda , sınıf tanımlama testi içinde statik değişken tanımı sağlamak için satır içi anahtar kelime de kullanılabilir .
Wormer

Yanıtlar:


72

Anladığım kadarıyla, C ++, statik const üyelerinin bir tamsayı türü olduğu sürece bir sınıf içinde tanımlanmasına izin veriyor.

Biraz haklısın. Sınıf bildiriminde statik const integrallerini başlatmanıza izin verilir, ancak bu bir tanım değildir.

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm

İlginç bir şekilde, std :: min çağrısına yorum yaparsam, kod derlenir ve bağlanır (test :: N'ye önceki satırda da başvurulsa bile).

Neler olduğuna dair bir fikriniz var mı?

std :: min parametrelerini const referansıyla alır. Değerlerine göre aldıysa, bu problemi yaşamazsınız, ancak bir referansa ihtiyacınız olduğu için bir tanıma da ihtiyacınız vardır.

İşte bölüm / ayet:

9.4.2 / 4 - Bir staticveri üyesi constintegral veya constnumaralandırma tipinde ise, sınıf tanımındaki beyanı, integral bir sabit ifade (5.19) olacak bir sabit başlatıcıyı belirtebilir . Bu durumda üye, integral sabit ifadelerde görünebilir. Üye, programda kullanılıyorsa yine de bir ad alanı kapsamında tanımlanacaktır ve ad alanı kapsam tanımı bir başlatıcı içermeyecektir .

Olası bir çözüm için Chu'nun cevabına bakın.


Görüyorum ki bu ilginç. Bu durumda, değeri beyan noktasında sağlamak ile tanım noktasında değeri sağlamak arasındaki fark nedir? Hangisi tavsiye edilir?
HighCommander4

Değişkeni asla "kullanmadığınız" sürece bir tanım olmadan kurtulabileceğinize inanıyorum. Bunu yalnızca sabit bir ifadenin parçası olarak kullanırsanız, değişken asla kullanılmaz. Aksi takdirde, başlıktaki değeri görebilmenin yanı sıra çok büyük bir fark görünmüyor - ki bu istediğiniz şey olabilir veya olmayabilir.
Edward Strange

2
Kısa cevap statik sabittir x = 1; bir değerdir, ancak bir değer değildir. Değer, derleme zamanında sabit olarak mevcuttur (onunla bir diziyi boyutlandırabilirsiniz) static const y; [başlatıcı yok] bir cpp dosyasında tanımlanmalıdır ve bir rvalue veya bir lvalue olarak kullanılabilir.
Dale Wilson

2
Bunu genişletebilseler / geliştirebilseler iyi olurdu. başlatılmış ama tanımlanmamış nesneler, bence, değişmez değerlerle aynı şekilde ele alınmalıdır. Örneğin, bir değişmezi 5bir const int&. Öyleyse neden test::NOP'leri karşılık gelen literal olarak ele almıyoruz ?
Aaron McDaid

İlginç açıklama, teşekkürler! Bu, C ++ 'da statik sabit int'in hala tamsayı # tanımlarının yerini almadığı anlamına gelir. enum her zaman yalnızca işaretli int'dir, bu nedenle tek tek sabitler için enum sınıflarını kullanmak gerekir. Sabit ve bilinen değerlere sahip sabit bir bildirimi birebir sabite dönüştürmek benim için oldukça açık olurdu, bu da sorunsuz bir şekilde derlenebilir. C ++ 'nın gidecek uzun bir yolu var ...
Johannes Overmann

51

Bjarne Stroustrup'un C ++ SSS sayfasındaki örneği , doğru olduğunuzu ve yalnızca adresi alırsanız bir tanıma ihtiyacınız olduğunu gösteriyor.

class AE {
    // ...
public:
    static const int c6 = 7;
    static const int c7 = 31;
};

const int AE::c7;   // definition

int f()
{
    const int* p1 = &AE::c6;    // error: c6 not an lvalue
    const int* p2 = &AE::c7;    // ok
    // ...
}

"Statik bir üyenin adresini sınıf dışı bir tanımı varsa (ve ancak) alabilirsin" diyor . Bu, aksi takdirde işe yarayacağını gösteriyor. Belki de min fonksiyonunuz bir şekilde perde arkasındaki adresleri çağırıyor.


2
std::minparametrelerini referans alarak alır, bu yüzden bir tanım gereklidir.
Rakete1111

AE bir şablon sınıfı AE <sınıf T> ise ve c7 bir int değil T :: size_type ise tanımı nasıl yazabilirim? Başlıkta "-1" olarak başlatılmış değerim var ama clang tanımsız değer diyor ve tanımı nasıl yazacağımı bilmiyorum.
Fabian

@Fabian Seyahat ediyorum ve bir telefonla ve biraz meşgulüm ... ama yorumunuzun kulağa yeni bir soru olarak yazılmasının en iyisi olacağını düşünüyorum. Aldığınız hatayı içeren bir MCVE yazın , ayrıca belki gcc'nin söylediklerini de yazın. Bahse girerim insanlar size neyin ne olduğunu çabucak söylerler.
HostileFork

@HostileFork: Bir MCVE yazarken, bazen çözümü kendiniz buluyorsunuz. Benim durumum için cevap, template<class K, class V, class C> const typename AE<K,V,C>::KeyContainer::size_type AE<K,V,C>::c7;KeyContainer'ın std :: vector <K> 'nin bir typedef olduğu yerdir. Bağımlı bir tür olduğu için tüm şablon parametrelerini listelemeli ve typename yazmalı. Belki birisi bu yorumu faydalı bulacaktır. Bununla birlikte, şimdi bunu bir DLL'ye nasıl aktaracağımı merak ediyorum çünkü şablon sınıfı elbette bir başlıkta. C7'yi dışa aktarmam gerekir mi ???
Fabian

24

Bunu yapmanın başka bir yolu, yine de tamsayı türleri için, sabitleri sınıfta numaralandırmalar olarak tanımlamaktır:

class test
{
public:
    enum { N = 10 };
};

2
Ve bu muhtemelen sorunu çözecektir. N, min () için bir parametre olarak kullanıldığında, sözde var olan bir değişkene atıfta bulunmak yerine bir geçici oluşturulmasına neden olur.
Edward Strange

Bunun özel yapılabilmesi avantajı vardı.
Agostino

11

Sadece int'ler değil. Ancak sınıf bildirimindeki değeri tanımlayamazsınız. Eğer varsa:

class classname
{
    public:
       static int const N;
}

.h dosyasında şunlara sahip olmalısınız:

int const classname::N = 10;

.cpp dosyasında.


2
Sınıf bildirimi içinde herhangi bir türden bir değişken tanımlayabileceğinizi biliyorum . Statik tamsayı sabitlerinin sınıf bildirimi içinde de tanımlanabileceğini düşündüğümü söyledim . Durum bu değil mi? Değilse, neden derleyici sınıf içinde tanımlamaya çalıştığım satırda bir hata vermiyor? Dahası, std :: cout satırı neden bağlayıcı hatasına neden olmazken std :: min satırı neden olur?
HighCommander4

Hayır, sınıf bildiriminde statik üyeler tanımlanamaz çünkü başlatma kodu yayar. Aynı zamanda kod da yayan bir satır içi işlevin aksine, statik bir tanım genel olarak benzersizdir.
Amardeep AC9MF

@ HighCommander4: static constSınıf tanımında integral üye için bir başlatıcı sağlayabilirsiniz . Ancak bu yine de o üyeyi tanımlamıyor . Ayrıntılar için Noah Roberts'ın cevabına bakın.
AnT

9

Sorunu aşmanın başka bir yolu şudur:

std::min(9, int(test::N));

(Sanırım Çılgın Eddie'nin cevabı sorunun neden var olduğunu doğru bir şekilde açıklıyor.)


5
hattastd::min(9, +test::N);
Tomilov Anatoliy

Yine de büyük soru şu: tüm bunlar optimal mi? Sizleri bilmiyorum çocuklar, ama tanımı atlamaktaki en büyük çekiciliğim, hiçbir bellek gerektirmemesi ve sabit statik kullanımında ek yük gerektirmemesi.
Opux

6

C ++ 11'den itibaren şunları kullanabilirsiniz:

static constexpr int N = 10;

Bu teorik olarak yine de sabiti bir .cpp dosyasında tanımlamanızı gerektirir, ancak adresini almadığınız sürece Nherhangi bir derleyici uygulamasının bir hata üretme olasılığı çok düşüktür;).


Ve değeri örnekteki gibi 'const int &' türünde bir bağımsız değişken olarak iletmeniz gerekirse ne olur? :-)
Wormer

Bu iyi çalışıyor. N'yi bu şekilde örneklemiyorsunuz , yalnızca geçici bir sabit referansına geçiriyorsunuz . wandbox.org/permlink/JWeyXwrVRvsn9cBj
Carlo Wood

C ++ 17 belki, C ++ 14 değil ve gcc 6.3.0 ve daha önceki sürümlerin önceki sürümlerinde C ++ 17 olmasa bile standart bir şey değil. Ama bundan bahsettiğiniz için teşekkürler.
Wormer

Ah evet, haklısın. Wandbox'ta c ++ 14'ü denemedim. Pekala, "Bu teorik olarak hala sabiti tanımlamanızı gerektiriyor" dediğim kısım budur. Yani, 'standart' olmadığı konusunda haklısınız.
Carlo Wood

3

C ++, statik const üyelerinin bir sınıf içinde tanımlanmasına izin verir

Hayır, 3.1 §2 diyor ki:

Bir bildirim, işlevin gövdesini (8.4) belirtmeden bir işlev bildirmediği, harici belirticiyi (7.1.1) veya bir bağlantı belirtimini (7.5) içermediği ve ne başlatıcı ne de işlev gövdesi içermediği sürece bir tanımdır , statik bir veri bildirir bir sınıf tanımında (9.4) üye, bir sınıf adı bildirimi (9.1), opak enum beyanı (7.2) veya typedef beyanı (7.1.3), kullanım beyanı (7.3.3). 3) veya kullanım yönergesi (7.3.4).

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.