C ++에서 참조로 전달하는 동안 매개 변수에 대한 기본값


117

참조로 매개 변수를 전달하는 동안 함수의 매개 변수에 기본값을 제공 할 수 있습니까? C ++에서

예를 들어 다음과 같은 함수를 선언하려고 할 때 :

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

이렇게하면 오류가 발생합니다.

오류 C2440 : '기본 인수': 'const int'에서 'unsigned long &'로 변환 할 수 없습니다. 'const'가 아닌 참조는 lvalue가 아닌 값에 바인딩 할 수 없습니다.


96
자비 롭게도 우리는 Google 스타일 가이드에 얽매이지 않습니다.

23
"이러지 마세요. Google 스타일 가이드 (및 기타)는 참조에 의한 비 상수 통과 금지"스타일 가이드에는 많은 주관적인 부분이 포함 된 것으로 알려져 있습니다. 이것은 그들 중 하나처럼 보입니다.
Johannes Schaub-litb

13
WxWidgets style guide says "don't use templates" and they have good reasons<-screw std :: vector, i say
Johannes Schaub-litb 2009-06-29

7
@jeffamaphone : Google 스타일 가이드에서는 스트림을 사용하지 말라고합니다. 당신도 그들을 피할 것을 제안 하시겠습니까?
rlbond 2009-06-29

10
망치는 스크루 드라이버를 놓는 끔찍한 도구이지만 못과 함께 사용할 수 있습니다. 기능을 오용 할 수 있다는 사실은 가능한 함정에 대해 경고 할뿐 사용을 금지해서는 안됩니다.
David Rodríguez-dribeas

답변:


103

const 참조에 대해서는 할 수 있지만 const가 아닌 참조에는 할 수 없습니다. 이는 C ++에서 임시 (이 경우 기본값)가 상수가 아닌 참조에 바인딩되는 것을 허용하지 않기 때문입니다.

한 가지 방법은 실제 인스턴스를 기본값으로 사용하는 것입니다.

static int AVAL = 1;

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

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

그러나 이것은 매우 제한된 실제 사용입니다.


const로 만들면 함수에 다른 주소를 전달할 수 있습니까? 아니면 State의 주소가 항상 0이고 의미가 없을까요?

참조를 사용하는 경우 주소를 전달하지 않습니다.

3
boost :: array to the rescue void f (int & x = boost :: array <int, 1> () [0]) {..} :)
Johannes Schaub-litb

1
실제로 전달되는 내용을 다루지 않으면?

2
@Sony 참조. 그것을 주소로 생각하는 것은 엉망입니다. 주소를 원하면 포인터를 사용하십시오.

33

이미 귀하의 답변에 대한 직접적인 의견 중 하나에서 언급되었지만 공식적으로 언급했습니다. 사용하려는 것은 과부하입니다.

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

함수 오버로드를 사용하면 추가 이점도 있습니다. 먼저 원하는 인수를 기본값으로 설정할 수 있습니다.

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

파생 클래스의 가상 함수에 대한 기본 인수를 재정의 할 수도 있습니다. 이렇게하면 오버로딩이 방지됩니다.

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
}

기본값을 실제로 사용해야하는 상황은 하나 뿐이며 생성자에 있습니다. 한 생성자를 다른 생성자에서 호출 할 수 없으므로이 기술은이 경우 작동하지 않습니다.


19
어떤 사람들은 기본 매개 변수가 과부하의 엄청난 곱셈보다 덜 끔찍하다고 생각합니다.
Mr. Boy

2
마지막에 다음과 같이 멘션 할 수 있습니다. d.f2(); // f2(int) called with '0' b.f2(); // f2(int) called with '0'
Pietro

4
마지막 문장에서 : C ++ 11부터는 위임 생성자를 사용하여 다른 생성자에서 하나의 생성자를 호출하여 코드 중복을 방지 할 수 있습니다.
Fred Schoen

25

선택적 인수를 제공하는 이전 C 방식이 여전히 있습니다. 존재하지 않을 때 NULL 일 수있는 포인터 :

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

아주 짧고 간단한이 방법이 정말 마음에 듭니다. 일반적으로 필요하지 않은 추가 정보, 통계 등을 반환하려는 경우 매우 실용적입니다.
uLoop

11

이 작은 템플릿은 다음과 같은 이점을 제공합니다.

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

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

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

private:
    T mValue;
};

그러면 다음을 수행 할 수 있습니다.

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

ByRef라이브 의 인스턴스화는 메모리 측면에서 어디입니까? 생성자와 같은 일부 범위를 벗어나면 파괴되는 임시 객체가 아닙니까?
Andrew Cheong

1
@AndrewCheong 그것의 전체 의도는 현장에서 건설되고 라인이 완성되면 파괴되는 것입니다. 참조를 예상하는 경우에도 기본 매개 변수를 제공 할 수 있도록 호출 기간 동안 참조를 노출하는 수단입니다. 이 코드는 활성 프로젝트에서 사용되며 예상대로 작동합니다.
Mike Weir

7

아니요, 불가능합니다.

참조로 전달하면 함수가 매개 변수 값을 변경할 수 있음을 의미합니다. 매개 변수가 호출자에 의해 제공되지 않고 기본 상수에서 오는 경우 변경해야하는 함수는 무엇입니까?


3
전통적인 FORTRAN 방식은 0 값을 변경하는 것이었지만 C ++에서는 발생하지 않습니다.
David Thornley

7

참조로 인수를 전달하는 데는 두 가지 이유가 있습니다. (1) 성능 (이 경우 const 참조로 전달하려는 경우)과 (2) 함수 내에서 인수 값을 변경할 수있는 기능이 필요하기 때문입니다.

나는 현대 건축에 서명되지 않은 장기를 전달하는 것이 당신을 너무 느리게 만드는 것이라고 매우 의심합니다. 그래서 나는 당신이 State메소드 내부의 값을 변경하려고한다고 가정합니다 . 컴파일러는 상수 0가 rvalue (오류 메시지에서 "non-lvalue")이고 변경할 수 없기 때문에 (오류 메시지에서) 변경할 수 없기 때문에 불평하고 있습니다 const.

간단히 말해, 전달 된 인수를 변경할 수있는 메서드를 원하지만 기본적으로 변경할 수없는 인수를 전달하려고합니다.

달리 말하면 비 const참조는 실제 변수를 참조해야합니다. 함수 시그니처 ( 0) 의 기본값 은 실제 변수가 아닙니다. 다음과 같은 문제가 발생합니다.

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

State메서드 내에서 실제로 변경하지 않으려 는 경우 간단히 const ULONG&. 그러나 당신은 그것으로부터 큰 성능 이점을 얻지 못할 것이므로 비 참조로 변경하는 것이 좋습니다 ULONG. 나는 당신이 이미를 반환하고 ULONG있음을 알고 있으며 그 값이 State필요한 수정 후의 값이라는 은밀한 의심을 가지고 있습니다 . 어떤 경우에는 간단하게 메서드를 다음과 같이 선언합니다.

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

물론, 나는 당신이 무엇을 쓰고 있는지, 어디에 있는지 잘 모르겠습니다. 그러나 그것은 또 다른 질문입니다.


6

함수 호출에 대한 매개 변수로 사용할 수없는 것과 같은 이유로 기본 매개 변수에 상수 리터럴을 사용할 수 없습니다. 참조 값에는 주소가 있어야하며 상수 참조 값은 필요하지 않습니다 (즉, r 값 또는 상수 리터럴 일 수 있음).

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

foo(0); // compiler error.

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

bar(0); // ok.

기본 값에 주소가 있고 괜찮은지 확인하십시오.

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

이 패턴은 변수 나 null이 될 수있는 매개 변수가있는 곳에서 몇 번 사용했습니다. 일반적인 접근 방식은 사용자가 포인터를 전달하도록하는 것입니다. 값을 채우지 않으려면 NULL 포인터를 전달합니다. 나는 null 객체 접근을 좋아합니다. 수신자 코드를 끔찍하게 복잡하게 만들지 않고 발신자 생활을 더 쉽게 만듭니다.


IMHO,이 스타일은 상당히 "냄새"입니다. 기본 인수가 실제로 정당화 될 수있는 유일한 경우는 생성자에서 사용할 때입니다. 다른 모든 경우에 함수 오버로드는 기본값과 관련된 다른 문제없이 정확히 동일한 의미를 제공합니다.
Richard Corden

1

그 이유는 기본값이 상수로 평가되고 참조로 전달 된 값은 상수 참조로 선언하지 않는 한 변경할 수 있어야하기 때문입니다.


2
기본값은 "상수로 평가"되지 않습니다.

1

또 다른 방법은 다음과 같습니다.

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

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

그러면 다음 호출이 가능합니다.

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

효과가있다. 기본적으로 NULL 포인팅 참조를 확인하기 위해 참조에서 값에 액세스하는 것입니다 (논리적으로는 NULL 참조가 없으며 사용자가 가리키는 것만 NULL입니다). 무엇보다 "참조"에서 작동하는 일부 라이브러리를 사용하는 경우 일반적으로 라이브러리 특정 참조 변수에 대해 동일한 작업을 수행하는 "isNull ()"과 같은 일부 API가 있습니다. 이러한 경우 해당 API를 사용하는 것이 좋습니다.
parasrish

1

OO의 경우 ... 주어진 클래스에 "Default"가 있고 "Default"라는 것은이 기본값 (값)이 부수적으로 선언되어야 함을 의미하며 다음은 기본 매개 변수로 사용할 수 있습니다. 예 :

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

당신의 방법에 ...

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

이 솔루션은이 경우 "Global default Pagination"이 단일 "참조"값이기 때문에 매우 적합합니다. 또한 "gobal-level"구성 (예 : 사용자 페이지 매김 탐색 기본 설정 등)과 같이 런타임에 기본값을 변경할 수 있습니다.


1

State에 대한 const 한정자로 가능합니다.

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

const ref로 long을 전달하는 것은 무의미하고 심지어 우스꽝스럽고 OP가 원하는 것을 얻지 못합니다.
Jim Balter 2017 년


0

이것에 대한 다소 더러운 트릭도 있습니다.

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

이 경우 다음과 std::move같이 호출해야합니다 .

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

이것은 약간의 재미있는 해결 방법 일 뿐이며 실제 코드에서 사용하는 것은 전혀 권장하지 않습니다!


0

이에 대한 해결 방법이 있습니다. 기본값에 대한 다음 예제를 참조하십시오 int&.

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

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

}

bool, double... 와 같이 원하는 사소한 데이터 유형에 대해 수행 할 수 있습니다 .


0

2 개의 과부하 함수를 정의합니다.

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

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

-3

virtual const ULONG Write (ULONG & State = 0, bool 시퀀스 = true);

대답은 매우 간단하고 설명하는 데 그다지 좋지는 않지만이 함수에서 수정 될 비 상수 매개 변수에 기본값을 전달하려면 다음과 같이 사용하는 것입니다.

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

3
NULL 포인터를 역 참조하는 것은 불법입니다. 이것은 경우에 따라 작동 할 수 있지만 불법입니다. 여기에서 자세한 내용을 읽어보십시오 parashift.com/c++-faq-lite/references.html#faq-8.7
Spo1ler
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.