Giá trị mặc định cho một tham số trong khi truyền tham chiếu trong C ++


116

Có thể đưa ra một giá trị mặc định cho một tham số của hàm trong khi chúng ta truyền tham số theo tham chiếu. trong C ++

Ví dụ: khi tôi cố gắng khai báo một hàm như:

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

Khi tôi làm điều này, nó sẽ báo lỗi:

lỗi C2440: 'đối số mặc định': không thể chuyển đổi từ 'const int' thành 'không dấu dài &' Một tham chiếu không phải là 'const' không thể bị ràng buộc với giá trị không phải là giá trị


96
Đáng thương thay, chúng tôi không bị ràng buộc bởi hướng dẫn phong cách Google.

23
"Đừng làm điều này. Hướng dẫn về phong cách của Google (và những người khác) cấm không vượt qua tham chiếu" tôi nghĩ rằng các hướng dẫn về phong cách được biết là có chứa nhiều phần chủ quan. Điều này trông giống như một trong số họ.
Julian Schaub - litb

13
WxWidgets style guide says "don't use templates" and they have good reasons<- vít std :: vector, tôi nói
Johannes Schaub - litb

7
@jeffamaphone: Google Style Guide cũng nói không sử dụng các luồng. Bạn sẽ đề nghị tránh chúng quá?
rlbond

10
Một cái búa là một công cụ khủng khiếp để đặt một tuốc nơ vít, nhưng nó hoàn toàn có thể sử dụng được với đinh. Việc bạn có thể sử dụng sai một tính năng sẽ chỉ cảnh báo bạn về những cạm bẫy có thể xảy ra chứ không cấm sử dụng tính năng này.
David Rodríguez - dribeas

Câu trả lời:


101

Bạn có thể làm điều đó cho một tham chiếu const, nhưng không phải cho một tham chiếu không const. Điều này là do C ++ không cho phép tạm thời (giá trị mặc định trong trường hợp này) bị ràng buộc với tham chiếu không const.

Cách này một chiều sẽ là sử dụng một thể hiện thực tế làm mặc định:

static int AVAL = 1;

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

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

nhưng đây là sử dụng thực tế rất hạn chế.


nếu tôi tạo nó const, nó có cho phép tôi chuyển một địa chỉ khác cho hàm không? hoặc địa chỉ của Nhà nước sẽ luôn là 0 và vô nghĩa như vậy?

Nếu bạn đang sử dụng tài liệu tham khảo, bạn không chuyển địa chỉ.

3
boost :: mảng để giải cứu void f (int & x = boost :: mảng <int, 1> () [0]) {..} :)
Johannes Schaub - litb

1
nếu không giải quyết những gì thực sự được thông qua?

2
@Sony Một tài liệu tham khảo. Thật đáng tiếc khi nghĩ về nó như một địa chỉ. Nếu bạn muốn một địa chỉ, sử dụng một con trỏ.

32

Nó đã được nói trong một trong những ý kiến ​​trực tiếp cho câu trả lời của bạn rồi, nhưng chỉ cần nêu chính thức. Những gì bạn muốn sử dụng là một quá tải:

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

Sử dụng chức năng quá tải cũng có lợi ích bổ sung. Trước tiên, bạn có thể mặc định bất kỳ đối số nào bạn muốn:

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...

Cũng có thể định nghĩa lại các đối số mặc định cho các hàm ảo trong lớp dẫn xuất, điều này làm quá tải tránh:

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
}

Chỉ có một tình huống trong đó một mặc định thực sự cần được sử dụng, và đó là trên một hàm tạo. Không thể gọi một nhà xây dựng từ một nhà xây dựng khác, và vì vậy kỹ thuật này không hoạt động trong trường hợp đó.


19
Một số người nghĩ rằng một param-param mặc định ít kinh khủng hơn một bội số khổng lồ của quá tải.
Cậu bé

2
Có thể cuối cùng, bạn đề cập: d.f2(); // f2(int) called with '0' b.f2(); // f2(int) called with '0'
Pietro

4
Trong câu lệnh cuối cùng: từ C ++ 11 trở đi, bạn có thể sử dụng các hàm tạo ủy nhiệm để gọi một hàm tạo từ các hàm khác để tránh trùng lặp mã.
Fred Schoen

25

Vẫn còn cách C cũ để cung cấp các đối số tùy chọn: một con trỏ có thể là NULL khi không xuất hiện:

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

Tôi thực sự thích phương pháp này, rất ngắn và đơn giản. Siêu thực tế nếu đôi khi bạn muốn trả lại một số thông tin, số liệu thống kê bổ sung, vv mà bạn thường không cần.
uLoop

11

Mẫu nhỏ này sẽ giúp bạn:

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

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

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

private:
    T mValue;
};

Sau đó, bạn sẽ có thể:

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

Trường hợp khởi tạo ByRefsống, trí nhớ khôn ngoan? Nó không phải là một đối tượng tạm thời sẽ bị phá hủy khi rời khỏi một phạm vi (như nhà xây dựng)?
Andrew Cheong

1
@AndrewCheong Toàn bộ ý định của nó là được xây dựng tại chỗ và bị phá hủy khi đường dây hoàn thành. Đó là một phương tiện để hiển thị một tham chiếu trong suốt thời gian của một cuộc gọi để có thể cung cấp một tham số mặc định ngay cả khi nó mong đợi một tham chiếu. Mã này được sử dụng trong một dự án hoạt động và các chức năng như mong đợi.
Mike Weir

7

Không, nó không thể.

Truyền bằng tham chiếu ngụ ý rằng hàm có thể thay đổi giá trị của tham số. Nếu tham số không được cung cấp bởi người gọi và xuất phát từ hằng số mặc định, chức năng được cho là thay đổi là gì?


3
Cách FORTRAN truyền thống sẽ là thay đổi giá trị 0, nhưng điều đó không xảy ra trong C ++.
David Thornley

7

Có hai lý do để truyền đối số bằng tham chiếu: (1) cho hiệu suất (trong trường hợp bạn muốn chuyển qua tham chiếu const) và (2) vì bạn cần khả năng thay đổi giá trị của đối số bên trong hàm.

Tôi rất nghi ngờ rằng việc vượt qua một thời gian dài không dấu trên các kiến ​​trúc hiện đại đang làm bạn chậm lại quá nhiều. Vì vậy, tôi cho rằng bạn đang có ý định thay đổi giá trị Statebên trong phương thức. Trình biên dịch đang phàn nàn vì hằng số 0không thể thay đổi, vì đó là một giá trị ("không phải giá trị" trong thông báo lỗi) và không thể thay đổi ( consttrong thông báo lỗi).

Nói một cách đơn giản, bạn muốn một phương thức có thể thay đổi đối số được truyền, nhưng theo mặc định, bạn muốn truyền một đối số không thể thay đổi.

Nói cách khác, các consttham chiếu không phải tham chiếu đến các biến thực tế. Giá trị mặc định trong chữ ký hàm ( 0) không phải là biến thực. Bạn đang gặp vấn đề tương tự như:

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

Nếu bạn không thực sự có ý định thay đổi Statebên trong phương thức, bạn có thể chỉ cần thay đổi nó thành a const ULONG&. Nhưng bạn sẽ không nhận được lợi ích hiệu suất lớn từ đó, vì vậy tôi khuyên bạn nên thay đổi nó thành không tham chiếu ULONG. Tôi nhận thấy rằng bạn đã trả lại a ULONGvà tôi có một nghi ngờ lén lút rằng giá trị của nó là giá trị Statesau khi có bất kỳ sửa đổi cần thiết nào. Trong trường hợp đó, tôi chỉ cần khai báo phương thức như vậy:

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

Tất nhiên, tôi không chắc chắn những gì bạn đang viết hoặc đến đâu. Nhưng đó là một câu hỏi cho một thời điểm khác.


6

Bạn không thể sử dụng một chữ không đổi cho một tham số mặc định vì cùng lý do bạn không thể sử dụng một tham số làm tham số cho lệnh gọi hàm. Giá trị tham chiếu phải có địa chỉ, giá trị tham chiếu không đổi không cần (nghĩa là chúng có thể là giá trị r hoặc bằng chữ không đổi).

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

foo(0); // compiler error.

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

bar(0); // ok.

Đảm bảo rằng giá trị mặc định của bạn có địa chỉ và bạn vẫn ổn.

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;
   }
}

Tôi đã sử dụng mẫu này một vài lần trong đó tôi có một tham số có thể là biến hoặc null. Cách tiếp cận thông thường là để người dùng vượt qua trong một con trỏ, đây là trường hợp. Họ chuyển vào một con trỏ NULL nếu họ không muốn bạn điền vào giá trị. Tôi thích cách tiếp cận đối tượng null. Nó làm cho cuộc sống của người gọi dễ dàng hơn mà không làm phức tạp mã callee.


IMHO, phong cách này khá "hôi hám". Lần duy nhất một đối số mặc định thực sự chính đáng là khi nó được sử dụng trong một hàm tạo. Trong mọi trường hợp khác, quá tải hàm cung cấp chính xác cùng một ngữ nghĩa mà không có bất kỳ vấn đề nào khác liên quan đến mặc định.
Richard Corden

1

Tôi nghĩ là không, và lý do là các giá trị mặc định được ước tính cho các hằng số và các giá trị được truyền bởi tham chiếu phải có thể thay đổi, trừ khi bạn cũng tuyên bố nó là tham chiếu không đổi.


2
Giá trị mặc định không được "đánh giá là hằng số".

1

Một cách khác có thể là như sau:

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

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

sau đó các cuộc gọi sau là có thể:

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;
}

Nó hoạt động. Về cơ bản, đó là truy cập tham chiếu giá trị để kiểm tra tham chiếu trỏ NULL (về mặt logic không có tham chiếu NULL, chỉ có điều bạn chỉ ra là NULL). Trên đỉnh, nếu bạn đang sử dụng một số thư viện hoạt động trên "tài liệu tham khảo", thì nhìn chung sẽ có một số API như "isNull ()" để thực hiện tương tự cho các biến tham chiếu cụ thể của thư viện. Và được đề xuất sử dụng các API đó trong những trường hợp như vậy.
ký sinh vào

1

Trong trường hợp OO ... Để nói rằng Lớp đã cho có và "Mặc định" có nghĩa là Mặc định (giá trị) này phải được khai báo một cách dễ dàng và sau đó có thể được sử dụng làm Thông số mặc định:

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

Theo phương pháp của bạn ...

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

Giải pháp này khá phù hợp vì trong trường hợp này, "Phân trang mặc định toàn cầu" là một giá trị "tham chiếu" duy nhất. Bạn cũng sẽ có quyền thay đổi các giá trị mặc định trong thời gian chạy như cấu hình "cấp độ yêu tinh", ví dụ: tùy chọn điều hướng phân trang của người dùng và vv ..


1

Có thể với vòng loại const cho Bang:

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

Thật vô nghĩa và thậm chí lố bịch khi vượt qua một chặng đường dài, và không đạt được những gì OP muốn.
Jim Balter

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

0

Ngoài ra còn có một mẹo khá bẩn cho việc này:

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

Trong trường hợp này, bạn phải gọi nó bằng std::move:

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

Nó chỉ là một cách giải quyết hài hước, tôi hoàn toàn không khuyên bạn nên sử dụng nó trong mã thực!


0

Tôi có một cách giải quyết cho vấn đề này, xem ví dụ sau về giá trị mặc định cho int&:

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

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

}

Bạn có thể làm điều đó cho bất kỳ loại dữ liệu tầm thường nào bạn muốn, chẳng hạn như bool, double...


0

Xác định 2 hàm quá tải.

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

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

-3

ảo const ULONG Viết (ULONG & State = 0, chuỗi bool = true);

Câu trả lời khá đơn giản và tôi không giỏi giải thích nhưng nếu bạn muốn chuyển một giá trị mặc định cho tham số không phải là const có thể sẽ được sửa đổi trong hàm này là sử dụng nó như sau:

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

3
Hủy bỏ hội nghị một con trỏ NULL là bất hợp pháp. Điều này có thể hoạt động trong một số trường hợp, nhưng nó là bất hợp pháp. Đọc thêm tại đây parashift.com/c++-faq-lite/references.html#faq-8.7
Spo1ler
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.