2013-02-25 4 views
3

gcc 4.7.1은 튜플에 대한 기본 클래스 최적화를 수행하지 않습니다. 이는 정말 유용한 기능이라고 생각합니다. 튜플은 4 바이트보다 큰 사실에서와 같이,이 경우공통 기본 클래스가 튜플에 대한 빈 기본 클래스 최적화를 깨뜨림

#include <tuple> 
#include <cstdint> 
#include <type_traits> 
class A { }; 
class B : public A { std::uint32_t v_; }; 
class C : public A { }; 
static_assert(sizeof(B) == 4,    "A has 32 bits."); 
static_assert(std::is_empty<C>::value,  "B is empty."); 
static_assert(sizeof(std::tuple<B, C>) == 4, "C should be 32 bits."); 

이 마지막 주장이 실패 : 그러나,이에 예기치 않은 제한이있을 나타납니다. 클래스 계층 구조를 깨지 않고이를 피할 수있는 방법이 있습니까? 아니면 다른 방법으로이 경우를 최적화하는 자체 쌍 구현을 구현해야합니까?

+3

[this] (http://flamingdangerzone.com/cxx11/2012/07/06/optimal-tuple-i.html)도 도움이 될 수 있습니다. –

+0

@AndyProwl, 재미있는 내용이지만 읽을 수는 있지만 즉시 연결되지 않습니다. . 두 튜플의 공통적 인 구현은 EBCO를 사용하지만, 여기에서 볼 수있는 EBCO 실패의 종류에 대해서는 알려주지 않습니다. – MvG

+5

해당 최적화를 적용 할 수 없습니다. 튜플에는 ** 두 개의'A' 객체가 있고 각 객체에는 다른 주소가 있어야합니다. 코드에 튜플을 추가 할 필요가 없다면 네 번째 유형 인'struct D : B, C {}; '를 추가하면 sizeof (D) sizeof (B)를 볼 수 있습니다. –

답변

6

빈 개체가 약간의 공간을 차지해야하는 이유는 두 개의 서로 다른 개체가 서로 다른 주소를 가져야하기 때문입니다. 그 예외는 파생 된 형식의 기본 하위 개체가 파생 된 전체 개체와 동일한 주소를 가질 수 있다는 것입니다 (파생 된 형식의 첫 번째 비 정적 구성원이 [*]과 동일한 형식이 아닌 경우). 빈 기본 최적화 하나는 기지 임의로 어떤 완전한 개체에 대한 sizeof x!=0 수 있도록 할 수 있습니다. 귀하의 경우에는

, 튜플은 A 하위 객체를 들고 빈베이스에 추가되는 추가 공간을 제거하는 것을 사용 C의 다른 하나는 B의 기본이지만, 그들은 다른이므로 주소가 달라야합니다. 두 개체 중 어느 것도 t의 기본 하위 개체가 아닙니다. 그는 다른 사람과 같은 주소를 가질 수 없습니다.

struct D : B, C {}; 

D의 크기는 BC 모두의 크기보다 엄격하게 클 것이다 : 당신은 또 다른 유형을 생성,이 효과를 볼 수 std::tuple를 사용할 필요가 없습니다. 실제로 두 개의 A 하위 객체가 있는지 확인하려면 A에 대한 포인터를 업 캐스트하려고하면 컴파일러가 방향에 다소 모호한 오류가 발생합니다.

[*] 표준 명시 같은 이유로,이 경우 금지 :

struct A {}; 
struct B : A { A a; }; 

다시 입력 B 각각 완전한 개체,이 경우에는 그들이 거기 두 A 객체이며 다른 주소가 있어야합니다.

2

실험을 통해 쉽게 볼 수 있으므로이 최적화는 다소 어려워 질 수 있습니다. 이 최적화는 tuple이 다른 클래스의 데이터 멤버 인 경우에 특히 유용합니다 (특히 해당 클래스를 컨테이너에 넣으려는 경우). 이 사실을 공개하지 않고이 tuple에 번들로 묶을 수있는 다른 데이터 멤버가 있습니까? 예를 들면 다음과 같습니다 보장 의미없이

class D 
{ 
    std::tuple<B, int, C, int> data_; 
public: 
    B& get_B() {return std::get<0>(data_);} 
    C& get_C() {return std::get<2>(data_);} 
    int& get_size() {return std::get<1>(data_);} 
    int& get_age() {return std::get<3>(data_);} 
}; 
나를 위해

하고, 이것이는 std::tuple<B, int, C, int>는 12 바이트이기 때문에 C 멀리 최적화지고 있습니다.

+0

불행히도 내 경우에 옵션이 아니지만 일반적으로 알고 유용합니다. 필자는이 경우에 절대적으로 필요한 것이 아니기 때문에 상속을 A에서 제외했다. 다른 경우에는 다른 접근법이 필요할 수도 있습니다. – MvG

+1

@MvG : 상속은 가장 학대받는 구조 중 하나입니다. 필요하지 않으면 그냥 사용하지 마십시오. –

+0

@ DavidRodríguez-dribeas : 제 경우에는 공통 조상이 간단한 태깅 유형이었습니다. 나는 템플리트 운영자가 모든 클래스에 적용 할 수 있지만 다른 클래스에는 적용 할 수 없도록하려고 의도했다. 대신 별도의 술어 템플리트를 구현할 것입니다. C++에 * 개념이 있다면 * 나는 이런 종류의 상속을 사용하는 것이 덜 유혹 될 것입니다. 그러나 나는이 교훈을 어쨌든 기억할 것이다. – MvG