구조체와 std :: pair를 사용하는 것의 차이점은 무엇입니까?


26

나는 경험이 제한적인 C ++ 프로그래머입니다.

를 사용하여 STL map일부 데이터를 저장하고 조작 한다고 가정하면 두 가지 데이터 구조 접근법 사이에 의미있는 차이 (성능도 있음)가 있는지 알고 싶습니다.

Choice 1:
    map<int, pair<string, bool> >

Choice 2:
    struct Ente {
        string name;
        bool flag;
    }
    map<int, Ente>

구체적 struct으로 간단한 대신에 오버 헤드가 pair있습니까?


18
A std::pair 구조체입니다.
Caleth

3
@ gnat : 이와 같은 일반적인 질문은 특히이 질문과 같은 특정 질문에 대한 특정 목표에 대한 적절한 목표 대상이 아닙니다.
Robert Harvey

18
@ Caleth- std::pair템플릿 입니다. std::pair<string, bool>구조체입니다.
피트 베커

4
pair의미가 전혀 없습니다. 미래에 당신을 포함하여 당신의 코드를 읽는 사람 e.first은 당신이 그것을 명시 적으로 지적하지 않으면 그 이름이 무엇 인지 알 수 없습니다. 나는 확고한 신자가 오전 pair에 매우 가난하고 게으른 추가했다 std, 그것은 생각되었다 때는 아무도 생각하지 않았다 "하지만 언젠가, 모든 사람들이이를 사용하려고하는 모든 것을 두 가지이며, 아무도 알 수 없습니다 어떤 사람의 코드 수단 ".
Jason C

2
@Snowman 오, 물론입니다. 여전히 map반복자가 유효한 예외가 아닌 것과 같은 것은 나쁜 일 입니다. ( "first"= key and "second"= value ... really std
Jason C

답변:


33

선택 1은 작은 "한 번만 사용"하는 것이 좋습니다. 본질적 std::pair으로 여전히 구조체입니다. 이 의견에서 알 수 있듯이 선택 1은 토끼 구멍 아래 어딘가에 못생긴 코드를 생성 thing.second->first.second->second하고 아무도 그것을 해독하고 싶지 않습니다.

선택 2는 다른 모든 것보다 낫습니다. 왜냐하면지도에있는 것들의 의미를 읽기가 더 쉽기 때문입니다. Ente가 갑자기 다른 플래그를 필요로하는 경우와 같이 데이터를 변경하려는 경우에도 더욱 유연합니다. 여기서 성능은 문제가되지 않습니다.


15

성능 :

따라 다릅니다.

특별한 경우 두 가지가 메모리에 유사하게 배치되므로 성능 차이가 없습니다.

매우 구체적인 경우 ( 데이터 멤버 중 하나로 빈 구조체 를 사용하는 경우 ) std::pair<>잠재적으로 EBO (빈베이스 최적화)를 사용할 수 있으며 구조체에 비해 크기가 작습니다. 크기가 작을수록 일반적으로 성능이 향상됩니다.

struct Empty {};
struct Thing { std::string name; Empty e; };

int main() {
    std::cout << sizeof(std::string) << "\n";
    std::cout << sizeof(std::tuple<std::string, Empty>) << "\n";
    std::cout << sizeof(std::pair<std::string, Empty>) << "\n";
    std::cout << sizeof(Thing) << "\n";
}

인쇄 : 32, 32, 40, 40 ideone .

참고 : 정규 쌍에 실제로 EBO 트릭을 사용하는 구현에 대해서는 알지 못하지만 일반적으로 튜플에 사용됩니다.


가독성 :

그러나 미세 최적화 외에도 명명 된 구조가보다 인체 공학적입니다.

내 말은, 간신히 이해하기 map[k].first어려운 동안 그렇게 나쁘지 않다는 것입니다 get<0>(map[k]). map[k].name우리가 무엇을 읽고 있는지를 즉시 나타내는 대조 .

실수로 유형을 바꾸는 것이 실제로 우려되기 때문에 유형을 서로 변환 할 수있는 것이 더 중요합니다.

당신은 또한 구조 대 명목 입력에 대해 읽을 수 있습니다. Ente는 예상되는 것들에 의해서만 작동 될 수있는 특정 유형이며 Ente, 작동 할 수있는 것은 무엇이든 작동 std::pair<std::string, bool>할 수 있습니다 . 관련된 의미 가 없기 때문에 예상 한 것을 포함 std::string하거나 bool포함하지 않는 경우에도 마찬가지 입니다.std::pair


유지 보수 :

유지 관리 측면 pair에서 최악입니다. 필드를 추가 할 수 없습니다.

tuple새 필드 를 추가 하는 한 모든 기존 필드는 여전히 동일한 인덱스로 액세스됩니다. 어느 때보 다도 필연적이지만 최소한 업데이트 할 필요는 없습니다.

struct확실한 승자입니다. 원하는 곳에 필드를 추가 할 수 있습니다.


결론적으로:

  • pair 두 세계 중 최악입니다
  • tuple 매우 구체적인 경우 (빈 유형) 약간의 가장자리가있을 수 있습니다.
  • 사용하십시오struct .

참고 : 게터를 사용하면 클라이언트가 알지 않아도 빈 기본 트릭을 사용할 수 있습니다 struct Thing: Empty { std::string name; }. 이것이 캡슐화 가 관심을 가져야 할 다음 주제 인 이유 입니다.


3
표준을 따르는 경우 EBO를 쌍으로 사용할 수 없습니다. 쌍 요소에 저장되어있는 회원 firstsecond빈을위한 곳이 없다, 자료 최적화에 킥에.
Revolver_Ocelot

2
@Revolver_Ocelot : EBO를 사용 하는 C ++ 을 작성할 수 없지만 pair컴파일러는 내장 기능을 제공 할 수 있습니다. 그러나 멤버로 간주되기 때문에 준수 할 수없는 경우 (예 : 주소 확인) 관찰 가능할 수 있습니다.
Matthieu M.

1
C ++ 20 [[no_unique_address]]은 멤버에 대해 EBO와 동등한 기능을 제공합니다.
underscore_d

3

std :: tie 및 C ++ 17의 구조적 바인딩을 사용하여 비 구조적 할당과 함께 함수의 반환 유형으로 사용될 때 가장 많이 빛납니다. std :: tie 사용 :

struct Ente {/*...*/};
std::map<int, Ente> map;
auto inserted_position = map.end();
auto was_inserted = false;
std::tie(inserted_position, was_inserted) = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

C ++ 17의 구조적 바인딩 사용 :

struct Ente {/*...*/};
std::map<int, Ente> map;
auto [inserted_position, was_inserted] = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

std :: pair (또는 tuple) 사용법의 나쁜 예는 다음과 같습니다.

using player_data = std::tuple<std::string, uint64_t, double>;
player_data player{};
/* ... */
auto health = std::get<2>(player);
/* ... */

위치 인덱스 2에 저장된 내용을 std :: get <2> (player_data) 호출 할 때 명확하지 않기 때문에 가독성을 기억하고 독자에게 코드의 역할을 분명히하는 것이 중요 합니다. 이것은 훨씬 더 읽기 쉽다고 생각하십시오.

struct player_data
{
    std::string name;
    uint64_t player_id;
    double current_health;
};
player_data player{};
/* ... */
auto health = player.current_health;
/* ... */

일반적으로 std :: pair 및 std :: tuple은 함수에서 둘 이상의 객체를 반환하는 방법으로 생각해야합니다. 내가 사용하는 (그리고 다른 많은 사람들도 사용하는 것을 보았던) 경험의 법칙은 std :: tuple 또는 std :: pair로 반환 된 객체는 객체를 반환하는 함수를 호출하는 컨텍스트 내에서만 "관련"된다는 것입니다 또는 데이터 구조를 연결하는 데이터 구조와 관련하여 (예 : std :: map은 스토리지 유형에 std :: pair를 사용함) 관계가 코드의 다른 곳에 존재하면 구조체를 사용해야합니다.

핵심 지침의 관련 섹션 :

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.