Örtük argüman dönüşümüne güvenmek tehlikeli midir?


10

C ++, bağımsız değişkenler beklenen türler değilse, parametre türlerinin eşleşen yapıcılarını otomatik olarak çağıran bir özelliğe sahiptir (doğru adını anlayamıyorum).

Bunun çok temel bir örneği, std::stringbir const char*argümanla beklenen bir işlevi çağırmaktır . Derleyici, uygun kurucuyu çağırmak için otomatik olarak kod oluşturur std::string.

Merak ediyorum, düşündüğüm kadar okunabilirlik için kötü mü?

İşte bir örnek:

class Texture {
public:
    Texture(const std::string& imageFile);
};

class Renderer {
public:
    void Draw(const Texture& texture);
};

Renderer renderer;
std::string path = "foo.png";
renderer.Draw(path);

Bu iyi mi? Yoksa çok mu ileri gidiyor? Eğer yapmamam gerekirse, bir şekilde Clang veya GCC'yi uyarması yapabilir miyim?


1
ya Draw daha sonra bir dize sürümü ile aşırı yüklenmişse?
cırcır ucube

1
@Dave Rager'ın cevabı başına, bunun tüm derleyicilerde derleneceğini sanmıyorum. Cevabı hakkındaki yorumuma bakın. Görünüşe göre, c ++ standardına göre, bunun gibi örtük dönüşümleri zincirleyemezsiniz. Yalnızca bir dönüşüm gerçekleştirebilirsiniz ve daha fazlasını yapamazsınız.
Jonathan Henson

Tamam üzgünüm, aslında bunu derlemedim. Örneği güncelledi ve hala korkunç, IMO.
futlib

Yanıtlar:


24

Buna, dönüştürücü bir kurucu (veya bazen örtük kurucu veya örtük dönüşüm) denir.

Bu olduğunda uyarmak için bir derleme zamanı anahtarının farkında değilim, ancak önlenmesi çok kolay; sadece explicitanahtar kelimeyi kullanın .

class Texture {
public:
    explicit Texture(const std::string& imageFile);
};

Dönüştürücü kurucuların iyi bir fikir olup olmadığı konusunda: O bağlıdır.

Örtük dönüştürmenin mantıklı olduğu durumlar:

  • Sınıf, örtük olarak inşa edilip edilmediğini umursamayacağınız kadar ucuzdur.
  • Bazı sınıflar kavramsal olarak argümanlarına benzer ( örtük olarak dönüştürebildiği ile std::stringaynı kavramı yansıtmak gibi const char *), dolaylı dönüşüm mantıklıdır.
  • Örtülü dönüşüm devre dışı bırakılırsa bazı sınıfların kullanımı çok daha rahatsız edici olur. (Bir dize hazır bilgisini her geçmek istediğinizde açıkça std :: string'i çağırmayı düşünün. Boost'un Parçaları benzerdir.)

Örtük dönüştürmenin daha az anlamlı olduğu durumlar:

  • İnşaat pahalıdır (grafik dosyasının yüklenmesi ve ayrıştırılması gereken Doku örneğiniz gibi).
  • Sınıflar kavramsal olarak argümanlarından çok farklıdır. Örneğin, boyutunu bağımsız değişken olarak alan dizi benzeri bir kapsayıcı düşünün:
    sınıf FlagList
    {
        FlagList (int initial_size); 
    };

    void SetFlags (const FlagList & flag_list);

    int main () {
        // Şimdi bu açıkça görülmemesine rağmen derleniyor
        // ne yapıyor.
        SetFlags (42);
    }
  • Yapının istenmeyen yan etkileri olabilir. Örneğin, bir AnsiStringsınıf gerekir değil dolaylı bir den inşa UnicodeStringUnicode ve ANSI dönüşüm bilgilerini kaybedebilir çünkü.

Daha fazla okuma:


3

Bu, bir yanıttan çok bir yorumdur, ancak bir yorum koymak için çok büyüktür.

İlginçtir, g++bunu yapmama izin vermiyor:

#include <iostream>
#include <string>

class Texture {
        public:
                Texture(const std::string& imageFile)
                {
                        std::cout << "Texture()" << std::endl;
                }
};

class Renderer {
        public:
                void Draw(const Texture& texture)
                {
                        std::cout << "Renderer.Draw()" << std::endl;
                }
};

int main(int argc, char* argv[])
{
        Renderer renderer;
        renderer.Draw("foo.png");

        return 0;
}

Aşağıdakileri üretir:

$ g++ -o Conversion.exe Conversion.cpp 
Conversion.cpp: In function int main(int, char**)’:
Conversion.cpp:23:25: error: no matching function for call to Renderer::Draw(const char [8])’
Conversion.cpp:14:8: note: candidate is: void Renderer::Draw(const Texture&)

Ancak, eğer satırı değiştirirseniz:

   renderer.Draw(std::string("foo.png"));

Bu dönüşümü gerçekleştirecektir.


Bu gerçekten de g ++ 'da ilginç bir "özellik". Bu ya doğru kod oluşturmak için derleme zamanında mümkün olduğunca tekrar tekrar aşağı gitmek yerine sadece bir tür derin denetler ya da g ++ komut ayarlanması gereken bir bayrak olduğunu varsayalım.
Jonathan Henson

1
en.cppreference.com/w/cpp/language/implicit_cast , g ++ 'ın standardı kesinlikle takip ettiği anlaşılıyor. OP koduyla biraz fazla cömert olan Microsoft veya Mac'in derleyicisi. Özellikle anlatım şu şekildedir: "Bir kurucuya veya kullanıcı tanımlı bir dönüşüm işlevine ilişkin argüman dikkate alındığında, yalnızca bir standart dönüşüm sırasına izin verilir (aksi takdirde kullanıcı tanımlı dönüşümler etkili bir şekilde zincirlenebilir)."
Jonathan Henson

Evet, sadece gccderleyici seçeneklerini bazılarını test etmek için kodu birlikte attım (ki bu özel durumu ele alacak herhangi bir şey yok gibi görünüyor). Ben daha fazla içine bakmadı (çalışıyorum gerekiyordu :-) ama gccstandart ve bağlılık explicitanahtar kelime kullanımı bağlılık verilen muhtemelen gereksiz olarak kabul edildi.
Dave Rager

Örtük dönüşümler zincirlenmez ve Texturemuhtemelen örtük bir şekilde oluşturulmamalıdır (diğer yanıtlardaki yönergelere göre), bu nedenle daha iyi bir çağrı sitesi renderer.Draw(Texture("foo.png"));(beklediğim gibi çalıştığını varsayarak) olurdu .
Blaisorblade

3

Buna örtük tür dönüşümü denir. Genel olarak, gereksiz tekrarları engellediği için iyi bir şeydir. Örneğin, fazladan bir kod yazmak zorunda kalmadan otomatik olarak bir std::stringsürümünü alırsınız Draw. Kendisini Rendererdeğiştirmeden yeteneklerini genişletmenize izin verdiği için açık-kapalı prensibini takip etmeye de yardımcı olabilir Renderer.

Öte yandan, dezavantajları yoktur. Bir argümanın nereden geldiğini bulmak zor olabilir. Bazen başka durumlarda beklenmedik sonuçlar doğurabilir. Yani ne explicitkelime içindir. Yapıcıya koyarsanız Texture, örtük tür dönüşümü için bu yapıcıyı kullanmayı devre dışı bırakır. Örtük tür dönüşüm hakkında küresel olarak uyarmak için bir yöntemin farkında değilim, ama bu bir yöntemin var olmadığı anlamına gelmez, sadece gcc'nin anlaşılmaz derecede çok sayıda seçeneği vardır.

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.