std :: vector에서 상속하지 않아야합니다.


189

좋아, 이것은 고백하기가 정말로 어렵지만, 나는 지금부터 물려받을 강한 유혹을 받는다 std::vector.

벡터에 대해 약 10 개의 사용자 정의 알고리즘이 필요하며 벡터의 직접 멤버가되기를 바랍니다. 그러나 당연히 나는 나머지 std::vector인터페이스 를 원합니다 . 법을 준수하는 시민으로서의 첫 번째 아이디어 std::vectorMyVector수업에 회원 을 두는 것이 었습니다 . 그러나 모든 std :: vector의 인터페이스를 수동으로 제공해야합니다. 입력하기에 너무 많습니다. 다음으로 개인 상속에 대해 생각했기 때문에 메서드를 제공하는 대신 using std::vector::member공개 섹션에 여러 개를 작성했습니다 . 실제로 너무 지루합니다.

그리고 여기에, 나는 실제로 공개적으로 상속 할 수 있다고 생각 std::vector하지만이 클래스를 다형성으로 사용해서는 안된다는 경고를 문서에 제공합니다. 나는 대부분의 개발자들이 이것이 다형성으로 사용되어서는 안된다는 것을 이해하기에 충분히 유능하다고 생각합니다.

내 결정이 절대적으로 정당하지 않습니까? 그렇다면 왜 그렇습니까? 추가 멤버가 실제로 멤버이지만 벡터의 모든 인터페이스를 다시 입력하지는 않는 대안을 제공 할 수 있습니까 ? 나는 그것을 의심하지만, 가능하다면 나는 행복 할 것이다.

또한 일부 바보는 다음과 같은 것을 쓸 수 있다는 사실을 제외하고

std::vector<int>* p  = new MyVector

MyVector를 사용할 때 다른 현실적인 위험이 있습니까? 사실이라고 말하면 벡터에 대한 포인터를 취하는 함수를 상상하는 것과 같은 것을 버립니다 ...

글쎄, 나는 나의 사건을 진술했다. 나는 죄를 지었다. 이제 나를 용서할지 아닌지는 당신에게 달려 있습니다 :)


9
따라서 기본적으로 컨테이너 인터페이스를 다시 구현하기에는 너무 게으르다는 사실을 기반으로 일반적인 규칙을 위반해도 괜찮은지 묻습니다. 그렇다면 아닙니다. 그 쓴 약을 삼키고 제대로하면 두 세계를 모두 누릴 수 있습니다. 그 사람이되지 마십시오. 강력한 코드를 작성하십시오.
Jim Brissom

7
비 멤버 함수에 필요한 기능을 추가하고 싶지 않은 이유는 무엇입니까? 저에게는 이것이이 시나리오에서 가장 안전한 일입니다.
Simone

11
@Jim : std::vector의 인터페이스는 상당히 커서 C ++ 1x가 등장하면 크게 확장 될 것입니다. 그것은 몇 년 안에 타이핑하는 것이 많고 더 많이 확장 될 것입니다. 나는 이것이 봉쇄 대신에 상속을 고려해야하는 좋은 이유라고 생각합니다. 만약 그러한 기능들이 회원이어야한다는 전제를 따르는 경우 (의심 할 것입니다). STL 컨테이너에서 파생되지 않는 규칙은 다형성이 아니라는 것입니다. 그런 식으로 사용하지 않으면 적용되지 않습니다.
sbi

9
이 질문의 실제 고기는 한 문장에서 "나는 그들이 직접 벡터의 구성원이되기를 원한다"는 것이다. 문제의 다른 것은 실제로 중요하지 않습니다. 왜 이걸 "원한다"? 비 멤버로서이 기능을 제공하는 것의 문제점은 무엇입니까?
jalf

8
@JoshC : "주님"은 항상 "주님"보다 일반적으로 사용되었으며, 킹 제임스 성경에서 발견 된 버전이기도합니다. "). 지구상에서 무엇을 "맞춤법 오류"라고 부르겠습니까?
ruakh December

답변:


155

사실,의 ​​상속 상속에는 아무런 문제가 없습니다 std::vector. 이것이 필요하면 그렇게하십시오.

정말로 필요한 경우에만 그렇게하는 것이 좋습니다 . 자유 기능으로 원하는 것을 수행 할 수없는 경우에만 (예 : 상태를 유지해야 함).

문제는 MyVector새로운 실체라는 것입니다. 그것은 새로운 C ++ 개발자가 그것을 사용하기 전에 지옥이 무엇인지 알아야한다는 것을 의미합니다. 차이 무엇 std::vectorMyVector? 어느 쪽이 여기저기서 사용하는 것이 더 낫습니까? 내가 이동해야하는 경우 std::vectorMyVector? 그냥 사용할 수 swap()있습니까?

더 좋아 보이는 것을 만들기 위해 새로운 엔티티를 만들지 마십시오. 이러한 실체들 (특히, 그런 것들)은 진공 상태에서 살지 않을 것입니다. 그들은 끊임없이 증가하는 엔트로피와 혼합 환경에서 살 것입니다.


7
이것에 대한 나의 반론은 그가 이것을하기 위해 무엇을하고 있는지 알아야한다는 것입니다. 예를 들어, 하지 에 추가 데이터 멤버를 소개 MyVector하고 동의 기능에 전달하려고 std::vector&하거나 std::vector*. std :: vector * 또는 std :: vector &를 사용하여 어떤 종류의 복사 할당이 필요한 경우 새 데이터 멤버 MyVector가 복사되지 않는 슬라이스 문제가 있습니다. 기본 포인터 / 참조를 통해 스왑을 호출하는 경우에도 마찬가지입니다. 객체 슬라이싱을 위험에 빠뜨리는 상속 계층 구조는 나쁜 것으로 생각하는 경향이 있습니다.
stinky472

13
std::vector의 소멸자가 아니므로 virtual절대 상속해서는 안됩니다
André Fratelli

2
이런 이유로 std :: vector를 공개적으로 상속하는 클래스를 만들었습니다 .STL이 아닌 벡터 클래스가있는 오래된 코드가 있었고 STL로 이동하고 싶었습니다. 이전 클래스를 std :: vector의 파생 클래스로 다시 구현하여 이전 코드에서 이전 함수 이름 (예 : size () 대신 Count ())을 계속 사용하면서 std :: vector를 사용하여 새 코드를 작성할 수있었습니다. 기능. 나는 데이터 멤버를 추가하지 않았으므로 std :: vector의 소멸자는 힙에서 작성된 객체에 대해 잘 작동했습니다.
Graham Asher

3
@GrahamAsher base에 대한 포인터를 통해 객체를 삭제하고 소멸자가 가상이 아닌 경우 프로그램에 정의되지 않은 동작이 나타납니다. 정의되지 않은 동작의 한 가지 가능한 결과는 "테스트에서 제대로 작동했습니다"입니다. 또 다른 방법은 할머니에게 웹 브라우징 기록을 이메일로 보내는 것입니다. 둘 다 C ++ 표준을 준수합니다. 컴파일러, OS의 포인트 릴리스 또는 달의 위상에 따라 하나에서 다른 것으로 변경됩니다.
Yakk-Adam Nevraumont

2
@GrahamAsher 아니요, 가상 소멸자가없는 기본 포인터를 통해 객체를 삭제할 때마다 표준에서 정의되지 않은 동작입니다. 나는 당신이 생각하는 것을 이해합니다. 당신은 잘못되었습니다. "기본 클래스 소멸자가 호출되고 작동합니다."는이 정의되지 않은 동작의 가능한 증상 중 하나이며 가장 일반적인 것은 컴파일러가 일반적으로 생성하는 순진한 기계 코드이기 때문입니다. 이것은 안전 하거나 좋은 생각을하지 않습니다.
Yakk-Adam Nevraumont

92

전체 STL은 알고리즘과 컨테이너가 분리 되도록 설계되었습니다 .

이것은 const iterators, random access iterators 등과 같은 다른 유형의 반복자 개념으로 이어졌습니다.

따라서이 컨벤션을 수락 하고 작업중 인 컨테이너가 무엇인지 신경 쓰지 않는 방식으로 알고리즘을 설계 하는 것이 좋습니다. 그리고 수행 해야하는 특정 유형의 반복자 만 필요합니다. 작업.

또한 Jeff Attwood의 좋은 말로 안내해 드리겠습니다 .


63

std::vector공개적으로 상속받지 않는 주된 이유 는 가상 소멸자가 없기 때문에 자손의 다형 적 사용을 효과적으로 방지 할 수 있습니다. 특히,하는 수 없습니다 에 파생 객체 (파생 클래스에 구성원을 추가하지 않는 경우에도)에서 실제로 점, 아직 컴파일러는 일반적으로 대한 경고를 표시 할 수 없습니다.deletestd::vector<T>*

이러한 조건에서는 개인 상속이 허용됩니다. 따라서 아래에 표시된 것처럼 개인 상속을 사용하고 부모로부터 필요한 메소드를 전달하는 것이 좋습니다.

class AdVector: private std::vector<double>
{
    typedef double T;
    typedef std::vector<double> vector;
public:
    using vector::push_back;
    using vector::operator[];
    using vector::begin;
    using vector::end;
    AdVector operator*(const AdVector & ) const;
    AdVector operator+(const AdVector & ) const;
    AdVector();
    virtual ~AdVector();
};

먼저 대부분의 응답자가 지적한 것처럼 알고리즘을 리팩토링하여 작동하는 컨테이너 유형을 추상화하고 무료 템플릿 기능으로 남겨 두어야합니다. 이것은 일반적으로 알고리즘이 컨테이너 대신 인수로 반복자 쌍을 허용하도록하여 수행됩니다.


IIUC에서 가상 소멸자가없는 것은 파생 클래스가 소멸시 해제해야하는 리소스를 할당하는 경우에만 문제가됩니다. (모두에 대한 포인터를 통해 파생 된 객체의 소유권을 무의식적으로 가져 오는 컨텍스트는 시간이 될 때만 기본 소멸자를 호출하기 때문에 다형성 유스 케이스에서 해제되지 않습니다. 다른 재정의 된 멤버 함수에서 비슷한 문제가 발생하므로주의해야합니다. 기본 전화는 유효합니다. 그러나 추가 리소스가 없으면 다른 이유가 있습니까?
피터-복원 모니카

2
vector할당 된 스토리지는 문제가되지 않습니다. 결국 vector소멸자는에 대한 포인터를 통해 모두 호출됩니다 vector. 표준은 기본 클래스 표현식을 통해 무료 상점 오브젝트 를 금지 delete하는 것 입니다. 그 이유는 (de) 할당 메커니즘이 예를 들어 특정 크기의 객체에 대한 할당 영역이 여러 개인 경우 메모리 청크의 크기를 피연산자 가없는 것으로 추론하려고 시도 할 수 있기 때문입니다. 이 제한은 afaics이지만 정적 또는 자동 저장 기간이있는 객체의 정상적인 파괴에는 적용되지 않습니다. delete
피터-복원 모니카

@DavisHerring 나는 우리가 거기에 동의한다고 생각한다 :-).
피터-복원 모니카

@DavisHerring 아, 알다시피, 당신은 나의 첫번째 의견을 언급합니다. 그 의견에 IIUC가 있었고, 그것은 질문으로 끝났습니다. 나는 그것이 항상 금지되어 있다는 것을 나중에 보았다. (Basilevs는 "효과적으로 예방한다"는 일반적인 진술을했으며,이를 방지하는 구체적인 방법에 대해 궁금했습니다.) 그렇습니다. UB.
피터-모니 티 복원

@Basilevs 부주의했을 것입니다. 결정된.
ThomasMcLeod

36

당신이 이것을 고려하고 있다면, 당신은 이미 당신의 사무실에서 언어 교육자들을 죽였다. 그들을 방해하지 않는 이유는 무엇입니까?

struct MyVector
{
   std::vector<Thingy> v;  // public!
   void func1( ... ) ; // and so on
}

그러면 실수로 MyVector 클래스를 업 캐스팅 할 때 발생할 수있는 모든 실수를 피할 수 있으며 조금만 추가하면 모든 벡터 작업에 계속 액세스 할 수 있습니다 .v.


컨테이너와 알고리즘을 노출합니까? 위의 코스 답변을 참조하십시오.
bruno nery


19

무엇을 성취하기를 바라고 있습니까? 일부 기능 만 제공합니까?

이를 수행하는 C ++ 관용적 방법은 기능을 구현하는 일부 무료 기능을 작성하는 것입니다. 실제로 std :: vector가 필요하지 않을 가능성이 있습니다.실제로 구현하는 기능에 대해 . 이는 std :: vector에서 상속하려고하면 실제로 재사용 가능성을 잃고 있음을 의미합니다.

표준 라이브러리와 헤더를보고 작동 방식에 대해 묵상 할 것을 강력히 권합니다.


5
나는 확신하지 못한다. 제안 된 코드로 업데이트하여 이유를 설명해 주시겠습니까?
Karl Knechtel

6
@Armen : 미학을 제외하고는 좋은 이유가 있습니까?
snemarch

12
@Armen : 더 나은 미학과 더 큰 일반성은 무료 frontback기능도 제공하는 것 입니다. :) (또한 무료 beginendC ++ 0x 및 부스트 예제를 고려하십시오 .)
UncleBens

3
나는 여전히 무료 기능에 문제가 없다. STL의 "미학"이 마음에 들지 않으면 C ++이 미학적으로 잘못된 곳일 수 있습니다. 그리고 다른 많은 알고리즘은 여전히 ​​무료 기능이므로 일부 멤버 함수를 추가해도 문제가 해결되지 않습니다.
Frank Osterfeld

17
외부 알고리즘에서 많은 작업 결과를 캐시하기는 어렵습니다. 벡터의 모든 요소의 합을 계산하거나 벡터 요소를 계수로 사용하여 다항식을 풀어야한다고 가정합니다. 이러한 작업은 무겁고 게으름이 유용 할 것입니다. 그러나 컨테이너에서 줄 바꿈하거나 상속하지 않으면 소개 할 수 없습니다.
바실 레프

14

나는 거의 100 %의 맹목적으로 따라야 할 규칙이 거의 없다고 생각합니다. 당신이 그것에 대해 많은 생각을했던 것처럼 들리며, 이것이 갈 길이라고 확신합니다. 따라서 누군가 이렇게하지 말아야 할 특별한 이유가 없다면 계획을 진행해야한다고 생각합니다.


9
첫 번째 문장은 100 % 사실입니다. :)
Steve Fallows

5
불행히도 두 번째 문장은 그렇지 않습니다. 그는 그것에 대해 많은 생각을하지 않았다. 질문의 대부분은 관련이 없습니다. 그의 동기를 보여주는 유일한 부분은 "나는 그들이 직접 벡터의 구성원이되기를 원한다"입니다. 내가 원하는. 에 대한 이유없는 이유는 이 바람직하다. 그가 전혀 생각하지 않은 것 같은 소리 .
jalf December

7

자신의 방식으로 정의 의 숨겨진 세부 사항을 처리하기 때문에 또는 다른 클래스 대신에 해당 클래스의 객체를 사용해야하는 이데올로기 적 이유가없는 한, std::vector다른 방식으로 작동하는 클래스를 만들지 않는 한 상속 할 이유가 없습니다. 의 것. 그러나 C ++에서 표준 작성자는 상속 된 클래스가 벡터를 특정 방식으로 향상시키기 위해 활용할 수있는 인터페이스 (보호 멤버의 형태)를 제공하지 않았습니다 . 실제로, 그들은 확장을 필요로하거나 추가 구현을 미세 조정할 필요가 있는 특정 측면 을 생각할 방법이 없었기 때문에 어떤 목적 으로든 그러한 인터페이스를 제공 할 필요가 없었습니다.std::vectorstd::vectorstd::vectorstd::vector

두 번째 옵션의 이유 std::vector는 다형성이 아니기 때문에 단지 이데올로기 일 수 있으며 , 그렇지 않으면 std::vector공개 상속을 통해 또는 공개 멤버십을 통해 공개 인터페이스를 노출하는지 여부에 차이가 없습니다 . (자유로운 기능을 피할 수 있도록 객체의 상태를 유지해야한다고 가정하십시오). 소리가 적고 이데올로기 적 관점에서 볼 때 std::vectors는 일종의 "간단한 아이디어"인 것처럼 보이며 , 대신 가능한 다른 클래스의 객체 형태의 복잡성은 이데올로기 적으로 사용하지 않습니다.


좋은 대답입니다. SO에 오신 것을 환영합니다!
Armen Tsirunyan

4

실질적인 용어 : 파생 클래스에 데이터 멤버가 없으면 다형성 사용법이 아니라도 아무런 문제가 없습니다. 기본 클래스와 파생 클래스의 크기가 다르거 나 가상 함수 (v- 테이블을 의미)가있는 경우 가상 소멸자 만 필요합니다.

BUT 이론 : C ++ 0x FCD의 [expr.delete]에서 : 첫 번째 대안 (객체 삭제)에서 삭제할 객체의 정적 유형이 동적 유형과 다른 경우 정적 유형은 삭제할 객체의 동적 유형의 기본 클래스이며 정적 유형은 가상 소멸자를 갖거나 동작이 정의되지 않습니다.

그러나 문제없이 std :: vector에서 개인적으로 파생 할 수 있습니다. 다음 패턴을 사용했습니다.

class PointVector : private std::vector<PointType>
{
    typedef std::vector<PointType> Vector;
    ...
    using Vector::at;
    using Vector::clear;
    using Vector::iterator;
    using Vector::const_iterator;
    using Vector::begin;
    using Vector::end;
    using Vector::cbegin;
    using Vector::cend;
    using Vector::crbegin;
    using Vector::crend;
    using Vector::empty;
    using Vector::size;
    using Vector::reserve;
    using Vector::operator[];
    using Vector::assign;
    using Vector::insert;
    using Vector::erase;
    using Vector::front;
    using Vector::back;
    using Vector::push_back;
    using Vector::pop_back;
    using Vector::resize;
    ...

3
"기본 클래스와 파생 클래스의 크기가 다르면 가상 소멸자가 필요합니다. 이 주장은 실질적으로 정확하지만 이론적으로는 아닙니다
Armen

2
그렇습니다. 원칙적으로 여전히 정의되지 않은 동작입니다.
jalf

이것이 정의되지 않은 행동이라고 주장하면 증거 (표준의 인용문)를보고 싶습니다.
hmuelner

8
@hmuelner : 불행히도 Armen과 jalf는 이것에 맞습니다. 가입일 [expr.delete]은 C ++ 0X FCD에서하기 : 삭제 대상물의 정적 유형은 다이내믹 형 다르면 <인용> 첫번째 대안 (삭제 대상)에서, 정적 유형은 다이내믹 형의 기본 클래스이어야한다 삭제 될 객체와 정적 유형의 가상 소멸자가 있거나 동작이 정의되어 있지 않습니다. </ quote>
Ben Voigt

1
실제로 행동이 사소한 소멸자의 존재에 달려 있다고 생각했기 때문에 재미 있습니다 (특히, POD 클래스는 포인터 대베이스를 통해 파괴 될 수 있음).
벤 Voigt

3

좋은 C ++ 스타일을 따르는 경우 가상 함수가없는 것이 문제가 아니라 슬라이싱입니다 ( https://stackoverflow.com/a/14461532/877329 참조 ).

가상 기능이없는 것이 왜 문제가되지 않습니까? 함수는 delete소유권을 가지지 않기 때문에 함수가 받는 포인터를 시도해서는 안됩니다 . 따라서 엄격한 소유권 정책을 따르는 경우 가상 소멸자가 필요하지 않습니다. 예를 들어, 이것은 항상 가상 소멸자가 있거나 없거나 잘못된 것입니다.

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    delete obj;
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj); //Will crash here. But caller does not know that
//  ...
    }

반대로, 이것은 항상 가상 소멸자가 있거나없는 상태에서 작동합니다.

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj);
//  The correct destructor *will* be called here.
    }

팩토리에서 오브젝트를 작성하는 경우 팩토리 delete는 자체 힙을 사용할 수 있으므로 작업 대신 삭제 포인터를 리턴해야합니다 . 호출자는 형식을 a share_ptr또는로 얻을 수 있습니다 unique_ptr. 즉,하지 않습니다 delete당신이하지 않은 것을 직접 으로부터를 new.


2

네, 안전하지 않은 일을하지 않도록 조심하는 한 안전합니다 ... 저는 누군가가 새로운 벡터를 사용하는 것을 본 적이 없기 때문에 실제로는 괜찮을 것입니다. 그러나 C ++의 일반적인 관용구는 아닙니다 ....

알고리즘이 무엇인지에 대한 자세한 정보를 제공 할 수 있습니까?

때로는 디자인으로 한 길을 가고 결국 다른 길을 볼 수 없습니다-10 개의 새로운 알고리즘으로 벡터를 만들어야한다고 주장하면 알람 벨이 울립니다. 벡터가 구현할 수있는 알고리즘 또는 범용 벡터이며 응용 프로그램 별 함수를 포함하는 객체를 만들려고합니까?

나는 당신이 이것을해서는 안된다는 것을 말하고있는 것이 아닙니다. 알람 벨에 주어진 정보가 울리면 추상화에 문제가 있다고 생각하게하고 필요.


2

나는 또한 std::vector최근 부터 물려 받았으며 매우 유용하다는 것을 알았으며 지금까지 아무런 문제가 없었습니다.

내 클래스는 희소 행렬 클래스이므로 행렬 요소를 어딘가, 즉에 저장해야합니다 std::vector. 상속 한 이유는 모든 메소드에 대한 인터페이스를 작성하기에는 너무 게으르고 SWIG를 통해 Python에 클래스를 인터페이스하는 것입니다. 이미 좋은 인터페이스 코드가 std::vector있습니다. 이 인터페이스 코드를 처음부터 새로 작성하지 않고 클래스로 확장하는 것이 훨씬 쉽다는 것을 알았습니다.

나는 접근 방식을 볼 수있는 유일한 문제는 너무 많은 비 가상 소멸자로이 아니라 같은 내가 과부하에하고 싶은 몇 가지 다른 방법,, push_back(), resize(),insert() 등 개인 상속은 참으로 좋은 옵션이 될 수 있습니다.

감사!


10
내 경험에 의하면, 최악의 장기 손상은 종종 경솔한 것을 시도 사람들에 의해 발생하고, " 지금까지 (읽기 경험하지 않은 주목 ) 그것으로 어떤 문제".
Disillusioned

0

여기에 원하는 두 가지 방법을 더 소개하겠습니다. 하나는 포장하는 또 다른 방법 std::vector이며, 다른 하나는 사용자에게 아무것도 부술 수있는 기회를주지 않고 상속하는 방법입니다.

  1. std::vector많은 함수 래퍼를 작성하지 않고 다른 래핑 방법을 추가하겠습니다 .

#include <utility> // For std:: forward
struct Derived: protected std::vector<T> {
    // Anything...
    using underlying_t = std::vector<T>;

    auto* get_underlying() noexcept
    {
        return static_cast<underlying_t*>(this);
    }
    auto* get_underlying() const noexcept
    {
        return static_cast<underlying_t*>(this);
    }

    template <class Ret, class ...Args>
    auto apply_to_underlying_class(Ret (*underlying_t::member_f)(Args...), Args &&...args)
    {
        return (get_underlying()->*member_f)(std::forward<Args>(args)...);
    }
};
  1. dtor 문제 대신 std :: span 에서 상속 std::vector하고 dtor 문제를 피하십시오.

0

이 질문은 숨을 쉬지 않는 진주 클러치를 만들어내는 것이 보장되지만 실제로 표준 컨테이너에서 파생되는 것을 피하기 위해 또는 "불필요하게 개체를 곱하는"피할만한 이유는 없습니다. 가장 단순하고 짧은 표현이 가장 명확하고 가장 좋습니다.

파생 된 유형에 대해 모든 일반적인주의를 기울여야하지만 표준의 기준에 대해서는 특별한 것이 없습니다. 기본 멤버 함수를 재정의하는 것은 까다로울 수 있지만 가상이 아닌 기본과는 현명하지 않으므로 여기에는 특별한 것이 없습니다. 데이터 멤버를 추가하려는 경우 멤버가베이스의 컨텐츠와 일관성을 유지해야하는 경우 슬라이싱에 대해 걱정해야하지만 다시는베이스와 동일합니다.

표준 컨테이너에서 파생 된 것으로 특히 유용한 곳은 다른 생성자에 의해 혼란이나 납치의 가능성없이 필요한 초기화를 정확하게 수행하는 단일 생성자를 추가하는 것입니다. (나는 당신을 찾고 있습니다, initialize_list 생성자!) 그런 다음 결과 객체를 자유롭게 사용할 수 있습니다. 템플릿 인수를 파생 클래스에 바인딩하는 것을 방해하지 않는 한 걱정할 필요가 없습니다.

이 기술이 C ++ 20에서 즉시 유용한 곳은 예약입니다. 우리가 쓴 곳

  std::vector<T> names; names.reserve(1000);

우리는 말할 수있다

  template<typename C> 
  struct reserve_in : C { 
    reserve_in(std::size_t n) { this->reserve(n); }
  };

그리고 반원으로서도

  . . .
  reserve_in<std::vector<T>> taken_names{1000};  // 1
  std::vector<T> given_names{reserve_in<std::vector<T>>{1000}}; // 2
  . . .

(기본 설정에 따라) 그리고 reserve ()를 호출하기 위해 생성자를 작성할 필요가 없습니다.

( reserve_in기술적으로 C ++ 20을 기다려야 하는 이유 는 이전 표준에서 이동 중에 빈 벡터의 용량을 보존 할 필요가 없기 때문입니다. 감독으로 인정되고 합리적으로 수정 될 수 있습니다 우리는 또한 기존의 모든 구현이 실제로 이동에 걸쳐 용량을 보존하기 때문에 표준이 요구하지 않았기 때문에 수정 사항이 효과적으로 이전 표준으로 업데이트 될 것으로 예상 할 수 있습니다. 총-예약은 거의 항상 최적화입니다.)

어떤 사람들은 reserve_in 무료 함수 템플릿 가 더 낫다고 .

  template<typename C> 
  auto reserve_in(std::size_t n) { C c; c.reserve(n); return c; }

이러한 대안은 확실히 실행 가능하며 * RVO로 인해 때로는 훨씬 더 빠를 수도 있습니다. 그러나 파생 또는 자유 기능의 선택은 표준 구성 요소에서 파생되는 데 대한 기본이 아닌 미신이 아닌 자체 장점으로 이루어져야합니다. 위의 예제 사용에서 두 번째 형식 만 free 기능으로 작동합니다. 클래스 컨텍스트 외부에서 조금 더 간결하게 작성할 수 있습니다.

  auto given_names{reserve_in<std::vector<T>>(1000)}; // 2
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.