Statik constexpr char [] için tanımlanmamış referans


186

static const charSınıfımda bir dizi olmasını istiyorum. GCC şikayet etti ve bana kullanmam gerektiğini söyledi constexpr, ancak şimdi bana tanımsız bir referans olduğunu söylüyor. Ben dizi üyesi olmayan yaparsanız derler. Ne oluyor?

// .hpp
struct foo {
  void bar();
  static constexpr char baz[] = "quz";
};

// .cpp
void foo::bar() {
  std::string str(baz); // undefined reference to baz
}

1
Sadece bir önsezi, baz örneğin int ise işe yarıyor mu? O zaman erişebilir misin? Ayrıca bir hata olabilir.
FailedDev

1
@ Kabarcık: Soru: Hangi çeviri biriminde tanımlanacak? Cevap: Başlığı içeren her şey. Sorun: Bir tanım kuralını ihlal ediyor. İstisna: Derleme zamanı sabit integralleri başlıklarda "başlatılabilir".
Mooing Duck

int@MooingDuck kadar iyi derler . Üye olmayanlar kadar iyi çalışır. Bu da kuralı ihlal etmez mi?
Pubby

@ Pubby8: inthile. Üye olmayan olarak, C ++ 11 için kurallar değişmedikçe buna izin verilmemelidir (mümkün)
Mooing Duck

Görüşler ve upvotes göz önüne alındığında, bu soruya aşağıda eklediğim daha ayrıntılı bir cevap gerekiyordu.
Shafik Yaghmour

Yanıtlar:


188

Cpp dosyanıza ekleyin:

constexpr char foo::baz[];

Sebep: Beyannamenin yanı sıra statik üyenin tanımını da sağlamanız gerekir . Bildirim ve başlatıcı sınıf tanımının içine girer, ancak üye tanımının ayrı olması gerekir.


70
Bu tuhaf görünüyor ... derleyiciye daha önce sahip olmadığı bazı bilgileri
vines

32
.Cpp dosyasında sınıf bildiriminiz olduğunda daha da garip görünüyor! Alanı sınıf bildiriminde başlatırsınız, ancak yine de sınıfın altına constexpr char foo :: baz [] yazarak alanı " bildirmeniz " gerekir . Constexpr kullanan programcılar, garip bir ipucunu takip ederek programlarını derleyebilirler: tekrar beyan edin.
Lukasz Czerwinski

5
@LukaszCzerwinski: Aradığınız kelime "tanımla" dır.
Kerrek SB

5
Doğru, yeni bilgi yok: kullanarak beyandecltype(foo::baz) constexpr foo::baz;
kullanıcı değil

6
fo geçici hale getirilirse ifade neye benzeyecektir? Teşekkürler.
Hei

80

C ++ 17 satır içi değişkenleri tanıtır

C ++ 17 constexpr static, odr kullanılıyorsa hat dışı tanım gerektiren üye değişkenler için bu sorunu giderir . C ++ 17 öncesi ayrıntılar için bu cevabın ikinci yarısına bakın.

Teklif P0386 Satır İçi Değişkenler , inlinebelirteci değişkenlere uygulama yeteneği sunar . Bu durumda özellikle constexprima inlinestatik üye değişkenler için. Teklif şöyle diyor:

Satır içi belirtici değişkenlere olduğu kadar işlevlere de uygulanabilir. Satır içi bildirilen bir değişken, satır içi bildirilen bir işlevle aynı anlambilime sahiptir: aynı, birden çok çeviri biriminde tanımlanabilir, tek kullanımlık olduğu her çeviri biriminde tanımlanmalıdır ve programın davranışı sanki tam olarak bir değişken var.

ve değiştirilmiş [basic.def] p2:

Bir beyan, bir tanım olmadıkça
...

  • sınıf tanımının dışında statik bir veri üyesi bildirir ve değişken sınıf içinde constexpr belirteci ile tanımlanır (bu kullanım kullanımdan kaldırılır; bkz. [depr.static_constexpr]),

...

ve [depr.static_constexpr] ekleyin :

Önceki C ++ Uluslararası Standartları ile uyumluluk için, bir constexpr statik veri üyesi, başlatıcı olmadan sınıf dışında gereksiz yere yeniden bildirilebilir. Bu kullanım kullanımdan kaldırıldı. [ Misal:

struct A {
  static constexpr int n = 5;  // definition (declaration in C++ 2014)
};

constexpr int A::n;  // redundant declaration (definition in C++ 2014)

 - son örnek]


C ++ 14 ve öncesi

C ++ 03'te, yalnızca const integralleri veya const numaralandırma türleri için sınıf içi başlatıcılar sağlamamıza izin verildi, bunu kullanarak C ++ 11'de değişmez türlereconstexpr genişletildi .

C ++ 11, biz statik bir ad kapsamı tanımını sağlamak gerekmez constexprDeğilse üyesi ODR kullanılan , biz taslak C ++ 11 standart bölümünden görebilirsiniz 9.4.2 [class.static.data] diyor ( benim ön plana çıkıyor ):

[...] Değişmez türde bir statik veri üyesi, constexpr belirleyicisi ile sınıf tanımında bildirilebilir; öyleyse, bildirimi, atama ifadesi olan her başlatıcı yan tümcesinin sabit bir ifade olduğu bir küme ayracı veya eşit başlatıcısı belirtmelidir. [Not: Bu iki durumda da üye sabit ifadelerle görünebilir. —End not] Üye, programda odr (3.2) kullanılıyorsa ve ad alanı kapsam tanımlamasında bir başlatıcı bulunmayacaksa yine de bir ad alanı kapsamında tanımlanmalıdır.

O zaman soru şu, burada baz odr kullanılıyor :

std::string str(baz); 

ve cevap evet , bu yüzden bir isim alanı kapsamı tanımına da ihtiyacımız var.

Peki bir değişkenin odr kullanılıp kullanılmadığını nasıl belirleyebiliriz ? 3.2 [Basic.def.odr] bölümündeki orijinal C ++ 11 ifadeleri şöyle diyor:

Bir ifade, değerlendirilmemiş bir işlenen (Madde 5) veya bunun bir alt ifadesi olmadıkça potansiyel olarak değerlendirilir. Adı, potansiyel olarak değerlendirilen bir ifade olarak görünen bir değişken, sabit bir ifadede (5.19) görünme gereksinimlerini karşılayan bir nesne olmadığı ve lvalue-rvalue dönüşümü (4.1) hemen uygulanmadığı sürece odr kullanılır .

Bu yüzden baz, bir verim yapar sabit ifade ancak lvalue-için-rvalue bu nedeniyle uygun değildir çünkü hemen uygulanmaz dönüşüm bazbir dizisi olan. Bu, şu 4.1 konudaki [dönş. Lval] bölümünde ele alınmıştır :

İşlevsel olmayan, T tipi olmayan bir glvalue (3.10) bir ön değere dönüştürülebilir.53 [...]

Ne uygulanan dizi-to-pointer dönüşümü .

[Basic.def.odr] ifadesi, Bazı durumlar bu ifadede yer almadığı için Hata Raporu 712 nedeniyle değiştirilmiştir, ancak bu değişiklikler bu durumun sonuçlarını değiştirmez.


bu nedenle bu açık olan constexpronunla hiçbir ilgisi vardır? ( bazyine de sabit bir ifadedir)
MM

@MattMcNabb iyi constexpr , üye bir değilse, integral or enumeration typeancak aksi takdirde, evet, önemli olan sabit bir ifade olmasıdır .
Shafik Yaghmour

İlk paragrafta "ord-used" "odr-used" olarak okunmalıdır, inanıyorum, ama C ++ ile asla emin değilim
Egor Pasko

37

Bu gerçekten C ++ 11'de bir kusurdur - diğerleri açıkladığı gibi, C ++ 11'de statik bir constexpr üye değişkeni, diğer tüm constexpr küresel değişkenlerinin aksine, dış bağlantıya sahiptir, bu nedenle açıkça bir yerde tanımlanmalıdır.

Ayrıca, tüm uygulamalarda satır içi olabileceğinden, optimizasyonda derleme yaparken tanımlamalar olmadan statik constexpr üye değişkenleri ile çoğu zaman pratikte kurtulabileceğinizi belirtmek gerekir, ancak optimizasyon olmadan derlerseniz, programınız bağlantı kuramaz. Bu, bunu çok yaygın bir gizli tuzak yapar - programınız optimizasyonla iyi derlenir, ancak optimizasyonu kapatır kapatmaz (belki de hata ayıklama için) bağlantı kuramaz.

İyi haber olsa da - bu kusur C ++ 17'de düzeltildi! Yaklaşım biraz kıvrıktır: C ++ 17'de statik constexpr üye değişkenleri dolaylı olarak satır içi şeklindedir . Sonra satır içi değişkenlere uygulanan C ++ 17 yeni bir kavram olmakla birlikte, açık bir tanım hiçbir yerinde gerekmez etkili araçlar.


4
C ++ 17 bilgisi için. Bu bilgiyi kabul edilen cevaba ekleyebilirsiniz!
SR

5

Daha zarif bir çözüm şuna dönüşmüyor char[]:

static constexpr char * baz = "quz";

Bu şekilde 1 kod satırında tanım / bildirim / başlatıcı olabilir.


9
ile char[]kullanabilirsiniz sizeofile derleme zamanında dize uzunluğunu almak için char *size (bu durumda işaretçi türü, 1 genişliğini dönecektir) olamaz.
gnzlbg

2
Bu ayrıca ISO C ++ 11 ile sıkı olmak istiyorsanız uyarı verir.
Shital Shah

sizeofSorunu sergilemeyen ve "yalnızca başlık" çözümlerinde kullanılabilecek cevabımı görün
Josh Greifer

4

Statik üyelerin dış bağlantısı için geçici constexprçözümüm referans üye alıcıları kullanmaktır (@deddebme'nin cevabına bir yorum olarak ortaya çıkan @gnzlbg sorununun içine girmez).
Bu deyim benim için önemlidir, çünkü projelerimde birden fazla .cpp dosyası olmasını istemiyorum ve sayıyı #includes ve main()işlevden başka bir şeyden biriyle sınırlamaya çalışıyorum .

// foo.hpp
struct foo {
  static constexpr auto& baz() { return "quz"; }
};

// some.cpp

  auto sz = sizeof(foo::baz()); // sz == 4

  auto& foo_baz = foo::baz();  // note auto& not auto
  auto sz2 =  sizeof(foo_baz);    // 4
  auto name = typeid(foo_baz).name();  // something like 'char const[4]'

-1

Çevremde gcc vesionu 5.4.0. "-O2" eklenmesi bu derleme hatasını düzeltebilir. Gcc, optimizasyon isterken bu durumu ele alabilir.

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.