Başlıkta neden C ++ satır içi işlevler var?


120

NB Bu, satır içi işlevlerin nasıl kullanılacağı veya nasıl çalıştıkları ile ilgili bir soru değildir, daha çok neden oldukları gibi yapıldıklarıdır.

Bir sınıf üyesi işlevin bildiriminin bir işlevi tanımlaması gerekmez inline, bu yalnızca işlevin gerçek uygulamasıdır. Örneğin, başlık dosyasında:

struct foo{
    void bar(); // no need to define this as inline
}

Öyleyse neden bir sınıf işlevinin satır içi uygulaması başlık dosyasında olmak zorunda? Neden satır içi işlevi .cppdosyaya koyamıyorum ? Satır içi tanımı .cppdosyaya koymaya çalışırsam, şu satırlar boyunca bir hata alırdım:

error LNK2019: unresolved external symbol 
"public: void __thiscall foo::bar(void)"
(?bar@foo@@QAEXXZ) referenced in function _main 
1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe 
: fatal error LNK1120: 1 unresolved externals



@Charles İkinci bağlantımın benzer olduğunu söyleyebilirim, ancak inline'ın neden bu şekilde çalıştığının arkasındaki mantık hakkında daha fazla soruyorum.
thecoshman

2
Bu durumda, "satır içi" veya "başlık dosyalarını" yanlış anladığınızı düşünüyorum; iddialarınızın hiçbiri doğru değil. Bir üye işlevinin satır içi uygulamasına sahip olabilirsiniz ve satır içi işlev tanımlarını bir başlık dosyasına koyabilirsiniz, bu iyi bir fikir olmayabilir. Sorunuzu netleştirebilir misiniz?
CB Bailey

Düzenleme sonrası, sanırım inlinebir tanımda göründüğünde, ancak önceki bir beyanda bulunmadığında veya tam tersi durumlarla ilgili sorular soruyor olabilirsiniz . Öyleyse, bu yardımcı olabilir: stackoverflow.com/questions/4924912/…
CB Bailey

Yanıtlar:


122

Bir inlineişlevin tanımının bir başlık dosyasında olması gerekmez, ancak satır içi işlevler için tek tanımlama kuralı ( ODR ) nedeniyle, işlevin kullanıldığı her çeviri biriminde işlev için aynı bir tanım bulunmalıdır.

Bunu başarmanın en kolay yolu, tanımı bir başlık dosyasına koymaktır.

Bir işlevin tanımını tek bir kaynak dosyaya koymak istiyorsanız, bunu bildirmemelisiniz inline. Bildirilmemiş bir işlev inline, derleyicinin işlevi satır içi yapamayacağı anlamına gelmez.

Bir işlev bildirmeniz gerekip inlinegerekmediği, genellikle sizin için en mantıklı tanım kurallarının hangi sürümüne bağlı olarak yapmanız gereken bir seçimdir ; eklenmesi inlineve ardından sonraki kısıtlamalarla sınırlandırılması çok az mantıklıdır.


Ancak derleyici, .h dosyalarını içeren .cpp dosyasını derlemez ... böylece bir .cpp dosyasını derlediğinde hem yavaşlamaya hem de kaynak dosyalarına sahip olur. Diğer üstbilgi dosyaları sadece onların, böylece derleyici bu işlevlerin var olduğuna ve başka bir kaynak dosyada uygulanacağına 'güvenebilir'
thecoshman

1
Bu aslında benden çok daha iyi bir cevap +1!
sbi

2
@thecoshman: İki fark var. Kaynak dosya vs başlık dosyası. Geleneksel olarak, bir başlık dosyası genellikle bir çeviri biriminin temeli olmayan, ancak diğer kaynak dosyalardan # dahil edilen bir kaynak dosyasına başvurur. Sonra deklarasyon ve tanım var. Başlık dosyalarında veya 'normal' kaynak dosyalarında işlevlerin bildirimleri veya tanımları olabilir. Korkarım yorumunuzda ne sorduğunuzdan emin değilim.
CB Bailey

endişelenme, neden şimdi anlıyorum ... buna gerçekten kimin cevap verdiğinden emin değilim. Sizin ve @ Xanatos'un cevabının bir kombinasyonu benim için bunu açıkladı.
thecoshman

113

Buna bakmanın iki yolu var:

  1. Satır içi işlevler başlıkta tanımlanır, çünkü bir işlev çağrısını satır içi yapmak için derleyicinin işlev gövdesini görebilmesi gerekir. Deneyimsiz bir derleyicinin bunu yapması için, işlev gövdesinin çağrı ile aynı çeviri biriminde olması gerekir. (Modern bir derleyici, çeviri birimleri arasında optimizasyon yapabilir ve bu nedenle, işlev tanımı ayrı bir çeviri biriminde olsa bile bir işlev çağrısı satır içinde olabilir, ancak bu optimizasyonlar pahalıdır, her zaman etkinleştirilmez ve her zaman tarafından desteklenmez. derleyici)

  2. Başlıkta tanımlanan işlevler işaretlenmelidir, inlineçünkü aksi takdirde, başlığı içeren her çeviri birimi işlevin bir tanımını içerecektir ve bağlayıcı birden fazla tanımdan (Tek Tanım Kuralı'nın ihlali) şikayet edecektir. inlineAnahtar çok sayıda çeviri birimleri (özdeş) tanımları içeren, ve bu da bastırır.

İki açıklama, inlineanahtar kelimenin tam olarak beklediğiniz şeyi yapmadığı gerçeğine dayanıyor.

Bir C ++ derleyicisi, programın gözlemlenebilir davranışını değiştirmediği sürece, istediği zaman satır içi optimizasyonu uygulamakta (bir işlev çağrısını çağrılan işlevin gövdesiyle değiştirmek, çağrı ek yükünü korumak) özgürdür .

inlineAnahtar kelime yapar kolay fonksiyon tanımı birden çeviri birimlerinde görünür olmasını sağlayan, ancak derleyici anlamına gelmez anahtar sözcüğünü kullanarak, bu optimizasyon uygulanamadı derleyici için olan işlevi satır içi ve olmayan yok anahtar kelime kullanarak derleyicinin işlevi satır içine almasını yasaklayın.


23

Bu, C ++ derleyicisinin bir sınırıdır. Fonksiyonu başlığa koyarsanız, satır içine alınabileceği tüm cpp dosyaları fonksiyonunuzun "kaynağını" görebilir ve satır içi işlemi derleyici tarafından yapılabilir. Aksi takdirde, satır içi oluşturma bağlayıcı tarafından yapılmalıdır (her cpp dosyası bir obj dosyasında ayrı ayrı derlenir). Sorun şu ki, bağlayıcıda bunu yapmak çok daha zor olacak. "Şablon" sınıflarında / işlevlerinde benzer bir sorun vardır. Derleyici tarafından somutlaştırılmaları gerekir, çünkü bağlayıcı, bunların somutlaştırılmasında (özel bir sürümünü yaratmada) sorun yaşayabilir. Daha yeni bir derleyici / bağlayıcı, derleyicinin ilk geçişi yaptığı yerde "iki geçişli" bir derleme / bağlama yapabilir, ardından bağlayıcı işini yapar ve çözülmemiş şeyleri (satır içi / şablonlar ...) çözmek için derleyiciyi çağırır.


Ah anlıyorum! evet, bu sınıf için değil, kendi içinde satır içi işlevi kullanan, satır içi işlevleri kullanan diğer kodu. Yalnızca satır içi olan sınıfın başlık dosyasını görürler!
thecoshman

11
Bu yanıta katılmıyorum, bu bir C ++ derleyici sınırı değil; tamamen dil kurallarının nasıl belirlendiği. Dil kuralları basit bir derleme modeline izin verir ancak alternatif uygulamaları yasaklamaz.
CB Bailey

3
@Charles'a katılıyorum. Aslında, çeviri birimlerinde satır içi işlev gören derleyiciler vardır, bu nedenle bu kesinlikle derleyici sınırlamalarından kaynaklanmamaktadır.
sbi

5
Bu cevabın bazı teknik hataları var gibi görünse de, derleyicinin başlık dosyalarıyla nasıl çalıştığını görmeme yardımcı oldu.
thecoshman

10

Bunun nedeni, derleyicinin çağrının yerine onu bırakabilmesi için tanımı gerçekten görmesi gerektiğidir.

C ve C ++ 'nın çok basit bir derleme modeli kullandığını unutmayın; burada derleyici her zaman yalnızca bir çeviri birimi görür. (Bu, ihracat için başarısız olur, bu, yalnızca bir satıcının bunu gerçekten uygulamasının ana nedenidir.)


9

C ++ inlineanahtar sözcüğü yanıltıcıdır, "bu işlevi satır içi" anlamına gelmez. Bir işlev satır içi olarak tanımlanırsa, bu, tüm tanımlar eşit olduğu sürece birden çok kez tanımlanabileceği anlamına gelir. Bir işlevin inline, çağrıldığı noktada satır içi kod almak yerine çağrılan gerçek bir işlev olarak işaretlenmesi tamamen yasaldır .

Şablonlar için başlık dosyasında bir işlev tanımlamak gerekir, çünkü şablonlu bir sınıf gerçekten bir sınıf değildir, birden çok varyasyonunu yapabileceğiniz bir sınıf için bir şablondur . Derleyicinin, örneğin bir Foo sınıfı oluşturmak için Foo şablonunu kullandığınızda bir Foo<int>::bar()işlev yapabilmesi için , fiili tanımının görünür olması gerekir.Foo<T>::bar()


Ve bir sınıf için şablon olduğu için , buna şablon sınıf değil, sınıf şablonu denir .
sbi

4
İlk paragraf tamamen doğrudur (ve keşke "yanıltıcı" yı vurgulayabilseydim), ancak şablonlarda sıralı olmayanların gerekliliğini görmüyorum.
Thomas Edleson

Bazı derleyiciler bunu, işlevin muhtemelen satır içi yapılabileceğine dair bir ipucu olarak kullanır , ancak aslında, yalnızca siz onu beyan ettiğiniz için satır içi inlineolması inlinegaranti edilmez (veya satır içi olmayacağını garanti etmez).
Keith M

4

Bunun eski bir konu olduğunu biliyorum ama externanahtar kelimeden bahsetmem gerektiğini düşündüm . Yakın zamanda bu sorunla karşılaştım ve aşağıdaki gibi çözdüm

Helper.h

namespace DX
{
    extern inline void ThrowIfFailed(HRESULT hr);
}

Helper.cpp

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            std::stringstream ss;
            ss << "#" << hr;
            throw std::exception(ss.str().c_str());
        }
    }
}

6
Bu genellikle Tam Program Optimizasyonu (WPO) kullanmadığınız sürece işlevin aslında satır içi olmasına neden olmaz.
Chuck Walbourn

3

Çünkü onları satır içi yapmak için derleyicinin bunları görmesi gerekir. Ve üstbilgi dosyaları, diğer çeviri birimlerinde genellikle bulunan "bileşenlerdir".

#include "file.h"
// Ok, now me (the compiler) can see the definition of that inline function. 
// So I'm able to replace calls for the actual implementation.

1

Satır İçi İşlevler

C ++ 'da makro, satır içi işlevden başka bir şey değildir. Yani artık makrolar derleyicinin kontrolü altındadır.

  • Önemli : Sınıf içinde bir işlev tanımlarsak, otomatik olarak Satır İçi olur

Satır içi işlevin kodu çağrıldığı yerde değiştirilir, böylece işlev çağırmanın ek yükünü azaltır.

Bazı durumlarda, işlevin satır içi çalışması çalışamaz.

  • Satır içi fonksiyonun içinde statik değişken kullanılıyorsa.

  • İşlev karmaşıksa.

  • Özyinelemeli işlev çağrısı ise

  • İşlevin adresi dolaylı veya açık olarak alınmışsa

Sınıf dışında aşağıdaki gibi tanımlanan işlev satır içi olabilir

inline int AddTwoVar(int x,int y); //This may not become inline 

inline int AddTwoVar(int x,int y) { return x + y; } // This becomes inline

Sınıf içinde tanımlanan işlev de satır içi olur

// Inline SpeedMeter functions
class SpeedMeter
{
    int speed;
    public:
    int getSpeed() const { return speed; }
    void setSpeed(int varSpeed) { speed = varSpeed; }
};
int main()
{
    SpeedMeter objSM;
    objSM.setSpeed(80);
    int speedValue = A.getSpeed();
} 

Burada hem getSpeed ​​hem de setSpeed ​​işlevleri satır içi hale gelecektir


eh, belki bazı güzel bilgiler, ancak nedenini açıklamaya çalışmıyor . Belki yaparsın, ama netleştirmiyorsun.
thecoshman

2
Aşağıdaki ifade doğru değildir: "Önemli: Eğer sınıf içinde bir fonksiyon tanımlarsak, otomatik olarak" Satır içi "haline gelecektir. Bildirime / tanıma" satır içi "yazsanız bile, aslında satır içi olduğundan emin olabilirsiniz. Şablonlar için bile. Belki de derleyicinin "satır içi" anahtar kelimesini otomatik olarak varsaydığını, ancak takip etmek zorunda olmadığını ve fark ettiğim şey, çoğu durumda bu tür başlık içi tanımları satır içi yapmadığıdır, basit constexpr işlevleri için bile temel aritmetik.
Pablo Ariel

Yorumlarınız için teşekkürler ... Aşağıda, Düşünme C ++ micc.unifi.it/bertini/download/programmazione/… Sayfa 400'den satırlar .. Lütfen kontrol edin .. Kabul ediyorsanız lütfen oy verin. Teşekkürler ..... Sınıfların içinde satır içi Bir satır içi işlev tanımlamak için, normalde işlev tanımından önce satır içi anahtar sözcükle gelmelisiniz. Ancak, bu bir sınıf tanımının içinde gerekli değildir. Bir sınıf tanımının içinde tanımladığınız herhangi bir işlev otomatik olarak bir satır içi olur.
Saurabh Raoot

O kitabın yazarları istediklerini iddia edebilirler çünkü kod yazmazlar kitap yazarlar. Bu, taşınabilir 3B demolarımı, satır içi kodlardan mümkün olduğunca kaçınarak 64kb'den daha azına sığdırmak için derinlemesine analiz etmem gereken bir şey. Programlama dinle değil gerçeklerle ilgilidir, bu nedenle uygulamada olanları temsil etmiyorsa, bir "tanrı programcısının" bunu bir kitapta söylemesinin bir önemi yoktur. Ve çoğu C ++ kitabı, zaman zaman repertuarınıza eklemek için düzgün bir numara bulabileceğiniz kötü bir tavsiye derlemesine sahiptir.
Pablo Ariel

Hey @PabloAriel Teşekkürler ... Lütfen analiz edin ve bana bildirin .. Bu cevabı analize göre güncelleyebilirim
Saurabh Raoot
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.