Bir ostream için << operatörünü nasıl aşırı yükleyebilirim?


237

Matris işlemleri için C ++ küçük bir matris kitaplığı yazıyorum. Ancak benim derleyici şikayet, nerede önce değil. Bu kod 6 ay boyunca bir rafta bırakıldı ve arasında bilgisayarımı debian etch'ten lenny'e (g ++ (Debian 4.3.2-1.1) 4.3.2) yükselttim, ancak aynı g ++ ile bir Ubuntu sisteminde aynı problemim var .

İşte matris sınıfımın ilgili kısmı:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix);
    }
}

Ve "uygulama":

using namespace Math;

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) {

    [...]

}

Derleyici tarafından verilen hata budur:

matrix.cpp: 459: hata: 'std :: ostream ve Matematik :: Matrix :: operatörü << (std :: ostream &, const Math :: Matrix &)' tam olarak bir bağımsız değişken almalıdır

Ben bu hatayla biraz kafam karıştı, ama sonra tekrar benim C ++ 6 ay Java çok yaptıktan sonra biraz paslı oldu. :-)

Yanıtlar:


127

İşlevinizi olarak ilan ettiniz friend. Sınıfın bir üyesi değil. Matrix::Uygulamadan kaldırmalısınız . friendbelirtilen işlevin (sınıfın üyesi olmayan) özel üye değişkenlere erişebileceği anlamına gelir. İşlevi uygulama şekliniz, Matrixsınıf için yanlış olan bir örnek yöntemi gibidir .


7
Ve bunu Math ad alanı içinde de bildirmelisiniz (sadece Math ad alanı kullanarak değil).
David Rodríguez - dribeas

1
Neden operator<<ad alanında olmak zorunda Math? Genel isim alanında olması gerektiği anlaşılıyor. Derleyicimin isim alanında olmasını istediğini kabul ediyorum Math, ama bu benim için anlamlı değil.
Mark Lakata

Üzgünüz, ama neden arkadaş anahtar kelimesini burada kullandığımızı göremiyorum. Bir sınıfta arkadaş operatörü geçersiz kıldığını bildirirken, Matrix :: operator << (ostream & os, const Matrix & m) ile uygulayamayacağımız anlaşılıyor. Bunun yerine, sadece global operatör geçersiz kılma operatörünü kullanmalıyız << ostream & os, const Matrix & m) neden ilk etapta sınıf içinde bildirmek için uğraşasınız ki?
Patrick

139

Sadece size başka bir olasılıktan bahsediyorum: Bunun için arkadaş tanımlarını kullanmayı seviyorum:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
            [...]
        }
    };
}

İşlev otomatik olarak çevredeki ad alanına hedeflenecektir Math(tanımı bu sınıfın kapsamında görünmesine rağmen), bağımsız değişken bağımlı aramayı bu işleç tanımını bulmasını sağlayacak bir Matrix nesnesi ile operatörü << çağırmazsanız görünmez. Matrix dışındaki argüman türleri için görünmez olduğu için bu bazen belirsiz çağrılara yardımcı olabilir. Tanımını yazarken, adı muhtemelen uzun bir önekle nitelendirmeden ve benzer şablon parametreleri sağlamadan doğrudan Matrix'te tanımlanan adlara ve Matrix'in kendisine de başvurabilirsiniz Math::Matrix<TypeA, N>.


77

Mehrdad yanıtına eklemek için,

namespace Math
{
    class Matrix
    {
       public:

       [...]


    }   
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}

Uygulamanızda

std::ostream& operator<<(std::ostream& stream, 
                     const Math::Matrix& matrix) {
    matrix.print(stream); //assuming you define print for matrix 
    return stream;
 }

4
Bunun neden aşağı oy olduğunu anlamıyorum, bu operatörü bir arkadaş olarak bile değil, isim alanında olduğunu ve operatörü nasıl beyan edebileceğinizi açıklar.
kal

2
Mehrdad cevabının herhangi bir kod snippet'i yoktu, bu yüzden sadece isim alanının kendisinde sınıfın dışına taşıyarak işe yarayabilecekleri ekledim.
kal

Ne demek istediğini anlıyorum, sadece ikinci pasajına baktım. Ama şimdi operatörü sınıftan çıkardığınızı görüyorum. Önerin için teşekkürler.
Matthias van der Vlies

7
Sadece sınıfın dışında değil, aynı zamanda Math ad alanının içinde de doğru bir şekilde tanımlanmıştır . Ayrıca, "baskı" nın sanal olabileceği ve böylece baskı en türetilmiş kalıtım düzeyinde gerçekleşecek ek bir avantaja (belki bir Matris için değil, diğer sınıflarla) sahiptir.
David Rodríguez - dribeas

68

Sınıfı ele operator <<almaktan türetilen tüm sınıflar için aşırı yükten bahsettiğimizi varsayarsak (ve sınıf için aşırı yüklenmez ), üstbilgideki Math ad alanının dışında aşırı yük işlevini bildirmek daha mantıklıdır.std::ostreamMatrix<<Matrix

Bir arkadaş işlevini, yalnızca işlevsellik genel arabirimler üzerinden gerçekleştirilemiyorsa kullanın.

Matrix.h

namespace Math { 
    class Matrix { 
        //...
    };  
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);

Operatörün aşırı yüklenmesinin ad alanının dışında bildirildiğini unutmayın.

Matrix.cpp

using namespace Math;
using namespace std;

ostream& operator<< (ostream& os, const Matrix& obj) {
    os << obj.getXYZ() << obj.getABC() << '\n';
    return os;
}

Diğer taraftan, aşırı yük fonksiyonu eğer yok yani özel ve korumalı üyeler erişmesi gerekir arkadaşını yapılması gerekir.

math.h

namespace Math {
    class Matrix {
        public:
            friend std::ostream& operator<<(std::ostream&, const Matrix&);
    };
}

İşlev tanımını sadece yerine bir ad alanı bloğu içine almanız gerekir using namespace Math;.

Matrix.cpp

using namespace Math;
using namespace std;

namespace Math {
    ostream& operator<<(ostream& os, const Matrix& obj) {
        os << obj.XYZ << obj.ABC << '\n';
        return os;
    }                 
}

38

C ++ 14'te T :: print (std :: ostream &) sabitine sahip herhangi bir nesneyi yazdırmak için aşağıdaki şablonu kullanabilirsiniz; üyesi.

template<class T>
auto operator<<(std::ostream& os, T const & t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 

C ++ 20'de Kavramlar kullanılabilir.

template<typename T>
concept Printable = requires(std::ostream& os, T const & t) {
    { t.print(os) };
};

template<Printable T>
std::ostream& operator<<(std::ostream& os, const T& t) { 
    t.print(os); 
    return os; 
} 

ilginç bir çözüm! Bir soru - bu operatörün küresel kapsamda olduğu gibi nerede beyan edilmesi gerekiyor? Ben onu baştan çıkarmak için kullanılabilecek her türlü görünür olması gerektiğini düşünüyorum?
barney

@barney Bunu kullanan sınıflarla birlikte kendi ad alanınızda olabilir.
QuentinUK

std::ostream&Yine de geri dönemez misin , çünkü dönüş türü zaten?
Jean-Michaël Celerier

5
@ Jean-MichaëlCelerier Decltype, bu operatörün yalnızca t :: print mevcut olduğunda kullanılmasını sağlar. Aksi takdirde, işlev gövdesini derlemeye ve bir derleme hatası vermeye çalışır.
QuentinUK

Kavramlar sürümü eklendi, burada test edildi godbolt.org/z/u9fGbK
QuentinUK
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.