C ++ 'da başvuruya göre geçerken bir parametre için varsayılan değer


117

Parametreyi referans olarak iletirken bir fonksiyonun parametresine varsayılan bir değer vermek mümkün mü? C ++ ile

Örneğin, şöyle bir işlev bildirmeye çalıştığımda:

virtual const ULONG Write(ULONG &State = 0, bool sequence = true);

Bunu yaptığımda bir hata veriyor:

hata C2440: 'varsayılan bağımsız değişken': 'const int' değerinden 'işaretsiz uzun ve' 'const' olmayan bir başvuru l olmayan bir değere bağlanamaz


96
Merhametle, Google stil kılavuzuna bağlı değiliz.

23
"Bunu yapmayın. Google stil kılavuzu (ve diğerleri) referansla sabit olmayan geçişi yasaklıyor" bence stil kılavuzlarının birçok öznel parça içerdiği bilinmektedir. Bu onlardan birine benziyor.
Johannes Schaub - litb

13
WxWidgets style guide says "don't use templates" and they have good reasons<- vida std :: vektör, diyorum
Johannes Schaub - litb

7
@jeffamaphone: Google Stil Kılavuzu ayrıca akışları kullanmamayı söylüyor. Onlardan kaçınmayı da önerir misin?
rlbond

10
Çekiç, tornavidayı yerleştirmek için korkunç bir araçtır, ancak çivilerle oldukça kullanılabilir. Bir özelliği kötüye kullanabileceğiniz gerçeği, sizi yalnızca olası tuzaklar konusunda uyarmalı, ancak kullanımını engellememelidir.
David Rodríguez - dribeas

Yanıtlar:


103

Bunu const başvurusu için yapabilirsiniz, ancak const olmayan bir başvuru için yapamazsınız. Bunun nedeni, C ++ 'nın geçici (bu durumda varsayılan değer) const olmayan bir başvuruya bağlanmasına izin vermemesidir.

Bunu aşmanın bir yolu, varsayılan olarak gerçek bir örnek kullanmaktır:

static int AVAL = 1;

void f( int & x = AVAL ) {
   // stuff
} 

int main() {
     f();       // equivalent to f(AVAL);
}

ancak bu çok sınırlı pratik kullanıma sahiptir.


Sabit yaparsam, işleve farklı bir adres geçirmeme izin verir mi? yoksa Devletin adresi her zaman 0 ve bu kadar anlamsız mı olacak?

Referans kullanıyorsanız, adresleri iletmiyorsunuz.

3
boost :: array kurtarma boşluğuna f (int & x = boost :: array <int, 1> () [0]) {..} :)
Johannes Schaub - litb

1
adres değilse aslında ne geçiliyor?

2
@Sony A referansı. Bunu bir adres olarak düşünmek yazıyor. Bir adres istiyorsanız, bir işaretçi kullanın.

33

Cevabınıza yapılan doğrudan yorumlardan birinde zaten söylendi, ancak resmi olarak belirtmek için. Kullanmak istediğiniz şey aşırı yüklenmedir:

virtual const ULONG Write(ULONG &State, bool sequence);
inline const ULONG Write()
{
  ULONG state;
  bool sequence = true;
  Write (state, sequence);
}

Fonksiyon aşırı yüklerini kullanmanın ek faydaları da vardır. İlk olarak, istediğiniz herhangi bir argümanı varsayılan olarak belirleyebilirsiniz:

class A {}; 
class B {}; 
class C {};

void foo (A const &, B const &, C const &);
void foo (B const &, C const &); // A defaulted
void foo (A const &, C const &); // B defaulted
void foo (C const &); // A & B defaulted etc...

Türetilmiş sınıftaki sanal işlevlere varsayılan bağımsız değişkenleri yeniden tanımlamak da mümkündür, bu da aşırı yüklemeden kaçınır:

class Base {
public:
  virtual void f1 (int i = 0);  // default '0'

  virtual void f2 (int);
  inline void f2 () {
    f2(0);                      // equivalent to default of '0'
  }
};

class Derived : public Base{
public:
  virtual void f1 (int i = 10);  // default '10'

  using Base::f2;
  virtual void f2 (int);
};

void bar ()
{
  Derived d;
  Base & b (d);
  d.f1 ();   // '10' used
  b.f1 ();   // '0' used

  d.f2 ();   // f1(int) called with '0' 
  b.f2 ();   // f1(int) called with '0
}

Bir varsayılanın gerçekten kullanılması gereken tek bir durum vardır ve bu bir kurucu üzerindedir. Bir kurucuyu diğerinden çağırmak mümkün değildir ve bu nedenle bu teknik bu durumda çalışmaz.


19
Bazı insanlar varsayılan bir parametrenin, aşırı yüklenmelerin dev bir çarpımından daha az korkunç olduğunu düşünüyor.
Bay Boy

2
Belki sonunda şunu söyleyebilirsiniz: d.f2(); // f2(int) called with '0' b.f2(); // f2(int) called with '0'
Pietro

4
Son ifadede: C ++ 11'den itibaren, kod yinelemesini önlemek için bir kurucuyu diğerinden çağırmak için temsilci oluşturucuları kullanabilirsiniz.
Fred Schoen

25

Hala isteğe bağlı argümanlar sağlamanın eski C yolu var: yoksa NULL olabilen bir işaretçi:

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}

Bu yöntemi gerçekten çok seviyorum, çok kısa ve basit. Bazen genellikle ihtiyaç duymadığınız bazı ek bilgileri, istatistikleri vb. Döndürmek isterseniz çok pratik.
uLoop

11

Bu küçük şablon size şu konularda yardımcı olacaktır:

template<typename T> class ByRef {
public:
    ByRef() {
    }

    ByRef(const T value) : mValue(value) {
    }

    operator T&() const {
        return((T&)mValue);
    }

private:
    T mValue;
};

Ardından şunları yapabileceksiniz:

virtual const ULONG Write(ULONG &State = ByRef<ULONG>(0), bool sequence = true);

ByRefCanlı, hafıza açısından somutlaştırılma nerede ? Bazı kapsamdan çıktıktan sonra (kurucu gibi) yok olan geçici bir nesne değil mi?
Andrew Cheong

1
@AndrewCheong Tüm amacı yerinde inşa edilmek ve hat tamamlandığında imha edilmektir. Bu, bir referans beklediğinde bile varsayılan bir parametrenin sağlanabilmesi için bir çağrının süresi boyunca bir referansı göstermenin bir yoludur. Bu kod aktif bir projede kullanılır ve beklendiği gibi çalışır.
Mike Weir

7

Hayır, mümkün değil.

Başvuruya göre geçiş, işlevin parametrenin değerini değiştirebileceği anlamına gelir. Parametre arayan tarafından sağlanmadıysa ve varsayılan sabitten geliyorsa, işlevin değişmesi gereken nedir?


3
Geleneksel FORTRAN yöntemi, 0'ın değerini değiştirmek olurdu, ancak bu C ++ 'da olmaz.
David Thornley

7

Bir bağımsız değişkeni başvuruya göre iletmenin iki nedeni vardır: (1) performans için (bu durumda const başvurusuyla geçmek istersiniz) ve (2) çünkü işlev içindeki bağımsız değişkenin değerini değiştirme yeteneğine ihtiyacınız vardır.

Modern mimarilerde imzasız bir süre geçmenin sizi çok fazla yavaşlattığından şüpheliyim. Dolayısıyla State, yöntemin içindeki değerini değiştirmeyi düşündüğünüzü varsayıyorum . Derleyici şikayet ediyor çünkü sabit 0bir rvalue (hata mesajında ​​"non-lvalue") ve değiştirilemez (const hata mesajında) olduğu için değiştirilemiyor.

Basitçe ifade etmek gerekirse, geçirilen argümanı değiştirebilen bir yöntem istersiniz, ancak varsayılan olarak değiştirilemeyen bir argüman iletmek istersiniz.

Başka bir deyişle, constreferans olmayanların gerçek değişkenlere atıfta bulunması gerekir. İmzadaki ( 0) varsayılan değer gerçek bir değişken değildir. Aynı problemle karşılaşıyorsunuz:

struct Foo {
    virtual ULONG Write(ULONG& State, bool sequence = true);
};

Foo f;
ULONG s = 5;
f.Write(s); // perfectly OK, because s is a real variable
f.Write(0); // compiler error, 0 is not a real variable
            // if the value of 0 were changed in the function,
            // I would have no way to refer to the new value

StateYöntemin içinde gerçekten değişme niyetinde değilseniz, onu basitçe bir const ULONG&. Ancak bundan büyük bir performans avantajı elde etmeyeceksiniz, bu yüzden onu referans olmayan bir şekilde değiştirmenizi tavsiye ederim ULONG. Zaten a döndürdüğünüzü fark ULONGettim ve değerinin Stategerekli herhangi bir değişiklikten sonra değeri olduğuna dair sinsi bir şüphem var . Bu durumda yöntemi basitçe şöyle bildiririm:

// returns value of State
virtual ULONG Write(ULONG State = 0, bool sequence = true);

Elbette, ne yazdığından veya nereye yazdığından pek emin değilim. Ama bu başka bir zaman için başka bir soru.


6

Varsayılan bir parametre için sabit bir hazır bilgi kullanamazsınız, ancak aynı nedenden birini işlev çağrısı için parametre olarak kullanamazsınız. Referans değerlerinin bir adresi olmalıdır, sabit referans değerlerinin olması gerekmez (yani, r değerleri veya sabit değişmez değerler olabilirler).

int* foo (int& i )
{
   return &i;
}

foo(0); // compiler error.

const int* bar ( const int& i )
{
   return &i;
}

bar(0); // ok.

Varsayılan değerinizin bir adresi olduğundan ve iyi olduğunuzdan emin olun.

int null_object = 0;

int Write(int &state = null_object, bool sequence = true)
{
   if( &state == &null_object )
   {
      // called with default paramter
      return sequence? 1: rand();
   }
   else
   {
      // called with user parameter
      state += sequence? 1: rand();
      return state;
   }
}

Bu kalıbı, değişken veya boş olabilecek bir parametremde birkaç kez kullandım. Normal yaklaşım, bu durumda kullanıcının bir işaretçiyi geçmesini sağlamaktır. Değeri doldurmanızı istemiyorlarsa bir NULL işaretçisi geçirirler. Nesne yaklaşımını boşa çıkarmayı seviyorum. Aranan ucun kodunu aşırı derecede karmaşıklaştırmadan arayanların hayatını kolaylaştırır.


IMHO, bu tarz oldukça "kokulu". Varsayılan bir bağımsız değişkenin gerçekten haklı gösterilebileceği tek zaman, bir yapıcıda kullanıldığı zamandır. Diğer her durumda, işlev aşırı yüklemeleri, varsayılanlarla ilişkili diğer sorunların hiçbiri olmadan tam olarak aynı semantiği sağlar.
Richard Corden

1

Sanmıyorum ve bunun nedeni, varsayılan değerlerin sabitler olarak değerlendirilmesidir ve referansla iletilen değerler, siz de sabit bir referans olarak beyan etmedikçe, değişebilmelidir.


2
Varsayılan değerler "sabitler olarak değerlendirilmez".

1

Başka bir yol da şunlar olabilir:

virtual const ULONG Write(ULONG &State, bool sequence = true);

// wrapper
const ULONG Write(bool sequence = true)
{
   ULONG dummy;
   return Write(dummy, sequence);
}

daha sonra aşağıdaki çağrılar mümkündür:

ULONG State;
object->Write(State, false); // sequence is false, "returns" State
object->Write(State); // assumes sequence = true, "returns" State
object->Write(false); // sequence is false, no "return"
object->Write(); // assumes sequence = true, no "return"

1
void f(const double& v = *(double*) NULL)
{
  if (&v == NULL)
    cout << "default" << endl;
  else
    cout << "other " << v << endl;
}

İşe yarıyor. Temel olarak, NULL işaretleme referansını kontrol etmek için referanstaki değere erişmektir (mantıksal olarak NULL referansı yoktur, yalnızca işaret ettiğiniz şey NULL'dur). Üstelik, "referanslar" üzerinde çalışan bir kitaplık kullanıyorsanız, genellikle kitaplığa özgü referans değişkenleri için aynısını yapmak için "isNull ()" gibi bazı API'ler olacaktır. Ve bu gibi durumlarda bu API'lerin kullanılması önerilir.
parasrish

1

OO durumunda ... Verilen bir Sınıfın sahip olduğunu ve "Varsayılan" olduğunu söylemek, bu Varsayılanın (değerin) uygun şekilde bildirilmesi gerektiği ve ardından Varsayılan Parametre olarak usd olabileceği anlamına gelir. Ör:

class Pagination {
public:
    int currentPage;
    //...
    Pagination() {
        currentPage = 1;
        //...
    }
    // your Default Pagination
    static Pagination& Default() {
        static Pagination pag;
        return pag;
    }
};

Yönteminize göre ...

 shared_ptr<vector<Auditoria> > 
 findByFilter(Auditoria& audit, Pagination& pagination = Pagination::Default() ) {

Bu çözümler oldukça uygundur çünkü bu durumda "Global varsayılan Sayfalandırma" tek bir "referans" değeridir. Ayrıca, çalışma zamanında "gobal düzeyinde" bir yapılandırma gibi varsayılan değerleri değiştirme gücüne sahip olacaksınız, örn: kullanıcı sayfalandırma gezinme tercihleri ​​vb.


1

State için const niteleyicisi ile mümkündür:

virtual const ULONG Write(const ULONG &State = 0, bool sequence = true);

Sabit ref kadar uzun bir süre geçmek anlamsız ve hatta gülünçtür ve OP'nin istediğini başaramaz.
Jim Balter

0
void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, bool revealExtent = false);

0

Bunun için de oldukça kirli bir numara var:

virtual const ULONG Write(ULONG &&State = 0, bool sequence = true);

Bu durumda telefonla aramanız gerekir std::move :

ULONG val = 0;
Write(std::move(val));

Bu sadece komik bir çözüm, gerçek kodda kullanılmasını kesinlikle önermiyorum!


0

Bunun için bir çözümüm var, varsayılan değerle ilgili aşağıdaki örneğe bakın int&:

class Helper
{
public:
    int x;
    operator int&() { return x; }
};

// How to use it:
void foo(int &x = Helper())
{

}

Bunu istediğiniz herhangi bir önemsiz veri türü için yapabilirsiniz, örneğin bool, double...


0

2 aşırı yük fonksiyonu tanımlayın.

virtual const ULONG Write(ULONG &State, bool sequence = true);

virtual const ULONG Write(bool sequence = true)
{
    int State = 0;
    return Write(State, sequence);
}

-3

sanal sabit ULONG Yazma (ULONG & Durum = 0, bool dizisi = doğru);

Cevap oldukça basit ve açıklamakta pek iyi değilim, ancak const olmayan bir parametreye varsayılan bir değer geçirmek istiyorsanız, muhtemelen bu işlevde değiştirilecekse, şu şekilde kullanın:

virtual const ULONG Write(ULONG &State = *(ULONG*)0, bool sequence =
> true);

3
Bir BOŞ göstericinin referansının kaldırılması yasa dışıdır. Bu bazı durumlarda işe yarayabilir, ancak yasadışıdır. Daha fazlasını buradan okuyun parashift.com/c++-faq-lite/references.html#faq-8.7
Spo1ler
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.