C ++에서 튜플을 사용하는 것이 더 일반적이지 않은 이유는 무엇입니까?


124

왜 아무도 C ++, Boost Tuple Library 나 TR1 용 표준 라이브러리 에서 튜플을 사용하지 않는 것 같습니까? 나는 많은 C ++ 코드를 읽었고 튜플의 사용을 거의 보지 못했지만 튜플이 많은 문제를 해결할 수있는 곳을 자주 볼 수 있습니다 (보통 함수에서 여러 값을 반환).

튜플을 사용하면 다음과 같은 모든 종류의 멋진 작업을 수행 할 수 있습니다.

tie(a,b) = make_tuple(b,a); //swap a and b

이것은 확실히 이것보다 낫습니다.

temp=a;
a=b;
b=temp;

물론 항상 이렇게 할 수 있습니다.

swap(a,b);

하지만 세 개의 값을 회전하려면 어떻게해야합니까? 튜플을 사용하여이를 수행 할 수 있습니다.

tie(a,b,c) = make_tuple(b,c,a);

튜플을 사용하면 함수에서 여러 변수를 반환하는 것이 훨씬 쉬워지며, 이는 값을 바꾸는 것보다 훨씬 더 일반적인 경우입니다. 참조를 사용하여 값을 반환하는 것은 확실히 우아하지 않습니다.

내가 생각하지 않는 튜플에 큰 단점이 있습니까? 그렇지 않다면 왜 거의 사용되지 않습니까? 느린가요? 아니면 단지 사람들이 그들에게 익숙하지 않은 것일까 요? 튜플을 사용하는 것이 좋은 생각입니까?


17
: 영리 튜플 스와핑 해트트릭 +1
kizzx2

10
a = a ^ b; b = a ^ b; a = a ^ b;
Gerardo Marset 2011 년

3
IMO 튜플은 약한 타이핑 언어 또는 네이티브 구조 인 언어에서 편리합니다. 예를 들어 Python이나 PHP에서는 삶을 더 쉽게 만드는 반면 C ++에서는 타이핑이 너무 많고 (템플릿에서 구성하기에는) 너무 적은 이점이 있습니다.
doc

4
OP에 대한 의견 : 현재 받아 들여지는 답변은 이미 사실이 틀릴 정도로 쓸모가 없다고 생각합니다. 수락 된 답변의 선택을 재고 할 수 있습니다.
ulidtko 2014 년

8
@GerardoMarset 진심이야?
thesaint

답변:


43

아직 표준이 아니기 때문입니다. 비표준은 훨씬 더 큰 장애물이 있습니다. 프로그래머가 요구했기 때문에 Boost의 조각이 인기를 얻었습니다. (hash_map이 떠 오릅니다). 그러나 튜플은 편리하지만 사람들이 그것을 괴롭히는 것은 압도적이고 명확한 승리는 아닙니다.


1
사람들은 Boost의 다른 부분을 미친 듯이 사용하는 것 같습니다. 확실히 해시 맵은 일반적으로 튜플보다 훨씬 유용합니다.
Zifre

나는 당신이 보는 것에 대한 세부 사항을 모르지만 사람들이 미친 듯이 사용하는 부분은 그들이 정말로 정말로 원했던 기능이라고 생각합니다. 따라서 해시 맵, 계수 된 포인터 등의 인기도를 다시 추측합니다. 튜플은 편리하지만 구멍을 채우기 위해 튀어 나오는 것은 아닙니다. 그 결과는 분명하지 않습니다. 정확히 N 개의 개체를 얼마나 자주 회전해야합니까? (임의로 긴 벡터를 회전해야하는 것과는 반대로). 그리고 사람들은 참조로 반환 값을 전달하거나 작은 클래스 또는 구조체를 반환하는 데 익숙합니다.
Alan De Smet

20
그것은 actualy 11 표준 지금은 C ++의 부분 : en.cppreference.com/w/cpp/utility/tuple
로마 수시

124

냉소적 인 대답은 많은 사람들이 C ++로 프로그래밍하지만 더 높은 수준의 기능을 이해하거나 사용하지 않는다는 것입니다. 때로는 허용되지 않기 때문이지만 많은 사람들은 단순히 시도하거나 이해하지 않습니다.

부스트가 아닌 예 : 얼마나 많은 사람들이에있는 기능을 사용 <algorithm>합니까?

즉, 많은 C ++ 프로그래머는 단순히 C ++ 컴파일러를 사용하는 C 프로그래머이며 아마도 std::vectorstd::list. 그것이 사용이 boost::tuple더 일반적이지 않은 이유 중 하나 입니다.


18
-1 C ++ 프로그래머는이 대답이 소리를내는 것처럼 멍청하지 않기 때문입니다.
user541686 2014 년

5
@Mehrdad 상업적이든 아니든 많은 C ++ 코드를 살펴보면서 수많은 C ++ 자료를 읽었 기 때문에 "C ++"개발자의 상당 부분이 순수성을 얻을 수없는 C 개발자라고 말하는 것이 상당히 안전하다고 생각합니다. C 컴파일러. 예를 들어 템플릿은 대부분의 자료에서 거의 완전히 누락되었습니다 (제가 많이 좋아하는 것을 배웠습니다). 이상한 매크로 해킹이 일반적이며 네임 스페이스는 심각하게 과소 사용됩니다.
선명

5
말도 안되는 대답. 필요한 경우 따라 잡을 것입니다. 필요하지 않습니다. 그래서 그들은 사용되지 않습니다. 이해하기 어렵 기 때문에 사용하지 않는다는 것은 나쁘다.
Michael Chourdakis 2014

9
@Michael 말도 안되는 코멘트. Turing 언어의 전체 하위 집합을 얻은 후에는 프로그래밍에 아무것도 필요 하지 않습니다 . 사용 부족이 모든 사람이 더 높은 수준의 C ++ 구문을 이해하고 사용하지 않기로 선택했음을 의미하지는 않습니다.
Trey Jackson

4
Tbh 저는 가변 템플릿 메타 프로그래밍 외부에서 std :: tuple이 필요하지 않았습니다. 인생에서 "그 튜플 만 있다면"라고 생각하면서 슬픈 표정으로 앉아있을 필요가 없었습니다. 사실 튜플을 볼 때 나는 "도대체 누군가가 제정신이 필요한 것이 무엇인지 (나는 제정신이라고 생각하지 않을 것입니다)"라고 생각합니다. 메타 프로그래밍 외부의 사람들은이를 "익명 구조"로 사용하는 것 같습니다. 이는 매우 추하고 머릿속에 코드 품질과 유지 보수 가능성이 없음을 나타냅니다.
thesaint

23

C ++ 튜플 구문은 대부분의 사람들이 원하는 것보다 훨씬 더 장황 할 수 있습니다.

치다:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;

따라서 튜플을 광범위하게 사용하려면 모든 곳에서 튜플 typedef를 얻거나 모든 곳에서 성가신 긴 유형 이름을 얻습니다. 나는 튜플을 좋아합니다. 필요한 경우 사용합니다. 그러나 일반적으로 N 요소 인덱스 또는 멀티 맵을 사용하여 범위 반복기 쌍을 연결할 때와 같은 몇 가지 상황으로 제한됩니다. 그리고 그것은 일반적으로 매우 제한된 범위에 있습니다.

Haskell이나 Python과 비교할 때 모두 매우 추하고 엉망진창입니다. C ++ 0x가 여기에 도착하면 'auto'키워드 튜플이 훨씬 더 매력적으로 보이기 시작할 것입니다.

튜플의 유용성은 선언, 압축 및 압축 해제에 필요한 키 입력 수에 반비례합니다.


대부분의 사람들은 "네임 스페이스 향상을 사용"합니다. boost ::를 입력 할 필요가 없습니다. 튜플을 입력하는 것이 그다지 문제라고 생각하지 않습니다. 즉, 나는 당신이 요점이 있다고 생각합니다. auto는 더 많은 사람들이 튜플을 사용하도록 만들 수 있습니다.
Zifre

2
@Zifre : 문제는 네임 스페이스 오염을 강요하고 네임 스페이스를 파괴하기 때문에 헤더 파일 내에서 "사용 네임 스페이스 X"를 수행해서는 안된다는 것입니다.
Mr Fooz

1
아, 네, 헤더는 잊어 버렸습니다. 그러나 프로그램 코드 내부에서는 그것에 대해 걱정할 필요가 없습니다. 그리고 일단 C ++ 0x가 있으면 auto를 사용할 수있어 많은 타이핑을 제거 할 수 있습니다.
Zifre

19
나 뿐인가요? 나는 "boost ::"의 7 개의 문자를 입력하는 것을 저장하는 것이 아니라 그가 언급 한 것보다 오히려 다른 33 개의 문자를 의미한다고 생각한다 . 특히 네임 스페이스 범위가 지정되는 경우에는 클래스 이름 입력이 엄청나게 많습니다. 우스꽝스러운 예로 boost :: tuple <std :: string, std :: set <std :: string>, std :: vector <My :: Scoped :: LongishTypeName>>을 사용하십시오.
오우거 시편 33

10

저에게는 습관입니다. 튜플은 저에게 새로운 문제를 해결하지 못합니다. 단지 몇 가지만 이미 잘 처리 할 수 ​​있습니다. 가치를 바꾸는 것은 여전히 ​​구식 방식보다 더 쉽다고 느낍니다. 그리고 더 중요한 것은 "더 나은"교환 방법에 대해 생각하지 않는다는 것입니다. 있는 그대로 충분합니다.

개인적으로 튜플이 여러 값을 반환하는 훌륭한 솔루션이라고 생각하지 않습니다 struct.


4
"나는"더 나은 "교환 방법에 대해 정말로 생각하지 않는다."-나는 코드를 작성할 때 버그를 작성한다. 코드 복잡성을 줄이면 내가 작성하는 버그 수가 줄어 듭니다. 나는 똑같은 버그를 몇 번이고 만드는 것이 싫다. 예, 코드를 더 잘 <strike> 스왑 </>하는 방법에 대해 생각합니다 . 움직이는 부분이 적고 (LOC, 임시 변수, 잘못 입력 할 식별자), 더 읽기 쉬운 코드 좋은 코드.
sehe

동의하다. 자동 포인터 또는 스마트 포인터로 래핑 된 클래스는 형식 저장입니다. 튜플을 한 번 사용했지만 클래스를 사용하여 코드를 다시 작성했습니다. retValue.state는 retValue.get <0> ()보다 명확합니다.
Valentin Heinitz 2014 년

1
@sehe : 더 나은, 더 읽기 쉬운 코드를 작성하는 것도 제 목표입니다. 더 많은 유형의 구문을 추가하면 비용이 발생하며 "더 나은 스와핑"이 읽는 모든 코드 줄에 대해 더 많은 유형의 구문에 대해 생각하는 정신적 오버 헤드를 정당화하지 않는다고 생각합니다.
ojrac

8

하지만 세 개의 값을 회전하려면 어떻게해야합니까?

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

좋아요, 그래서 4 개의 etc 값으로, 결국 n- 튜플은 n-1 스왑보다 적은 코드가됩니다. 그리고 기본 스왑을 사용하면 컴파일러가 간단한 유형에 대해 해결할 수 있기를 바라지 만 3주기 템플릿을 직접 구현 한 경우 가질 수있는 4 대신 6 개의 할당을 수행합니다.

다음과 같이 스왑이 다루기 어렵거나 부적절한 시나리오를 생각 해낼 수 있습니다.

tie(a,b,c) = make_tuple(b*c,a*c,a*b);

포장을 풀기가 조금 어색합니다.

그러나 요점은 튜플이 좋은 가장 일반적인 상황을 처리하는 알려진 방법이 있으므로 튜플을 차지하는 데 큰 긴급 성이 없다는 것입니다. 다른 것이 없다면 다음과 같은 확신이 없습니다.

tie(a,b,c) = make_tuple(b,c,a);

6 개의 복사본을 만들지 않기 때문에 일부 유형에는 전혀 적합하지 않습니다 (컬렉션이 가장 분명함). 튜플이 "대형"유형에 대한 좋은 생각이라고 저를 설득하십시오.

여러 값을 반환하는 경우 값이 호환되지 않는 유형이면 튜플이 완벽하지만 호출자가 잘못된 순서로 가져올 수있는 경우 튜플을 좋아하지 않는 사람도 있습니다. 일부 사람들은 여러 반환 값을 전혀 좋아하지 않으며 더 쉽게 사용하여 사용을 장려하고 싶지 않습니다. 어떤 사람들은 인 / 아웃 매개 변수에 대해 명명 된 구조를 선호하며 야구 방망이로 튜플을 사용하도록 설득 할 수 없을 것입니다. 맛에 대한 설명이 없습니다.


1
벡터를 튜플과 바꾸고 싶지 않을 것입니다. 세 가지 요소를 교체하는 것이 두 번의 교체보다 튜플을 사용하는 것이 더 명확하다고 생각합니다. 여러 반환 값의 경우 out 매개 변수는 사악하고 구조체는 추가 입력이며 여러 반환 값이 필요한 경우가 있습니다.
Zifre

튜플 사용하는 이유 있습니다 (그리고 튜플 사용하는 이유 알고 있습니다 . 다른 사람들이 알고 있어도 사용하지 않는 이유를 추측하고 있습니다. 예를 들어 그들이 "out params are evil"에 동의하지 않기 때문에 ...
Steve Jessop

"tie (a, b, c) = make_tuple (b, c, a);"를 대체 할 수 있는지 아십니까? 의해 "tie (a, b, c) = tie (b, c, a);" ?
Rexxar

2
타이 (기술적으로 계층)는 상수가 아닌 참조로 만든 튜플입니다. 관련된 참조 중 일부가 동일한 참조를 가질 때 operator = 및 tie / tuple make에 대한 복사 생성자를 보장하는 부스트 문서를 찾을 수 없습니다. 그러나 그것이 당신이 알아야 할 것입니다. operator =의 순진한 구현은 분명히 매우 잘못 될 수 있습니다 ...
Steve Jessop

1
@Steve : 그리고 타이는 모두 복사 방지에 관한 것이므로 (복사 할 수없는 유형에 대해 작동해야합니다. LHS가 완전히 다른 것일 수 있음에 유의하십시오) 실제로 모든 것이 매우 잘못 될 것입니다 (POD가 아닌 클래스 객체를 생각해보십시오). temp를 사용하지 않고 동일한 논리를 작성하는 방법을 상상해보십시오.
sehe

7

많은 사람들이 지적했듯이 튜플은 다른 기능만큼 유용하지 않습니다.

  1. 스와핑 및 회전 기믹은 단순한 기믹입니다. 그들은 전에 그들을 본 적이없는 사람들에게 완전히 혼란스럽고, 거의 모든 사람들이기 때문에 이러한 속임수는 단지 열악한 소프트웨어 엔지니어링 관행 일뿐입니다.

  2. 튜플을 사용하여 여러 값을 반환하는 것은 명명 된 유형을 반환하거나 명명 된 참조를 사용하는 대안보다 훨씬 덜 자체 문서화됩니다. 이 자체 문서화가 없으면 반환 값이 상호 변환 가능하고 더 현명하지 않은 경우 반환 된 값의 순서를 혼동하기 쉽습니다.


6

모든 사람이 boost를 사용할 수있는 것은 아니며 TR1은 아직 널리 사용되지 않습니다.


3
많은 사람들이 Boost를 사용합니다. 그 사람들은 튜플도 사용할 수 있습니다.
Zifre

3
왜 사람들이 그것을 사용하지 않는지 물었고 나는 한 가지 대답을했습니다.
Brian Neal

2
투표자에게 : 저는 부스트를 사용하는 것이 정치적으로 불가능한 곳에서 일하고 있습니다. 현재까지도 우리가 사용하는 컴파일러 도구 체인 (임베디드 시스템 용)은 TR1 / C ++ 11을 지원하지 않습니다.
Brian Neal

5

임베디드 시스템에서 C ++를 사용할 때 Boost 라이브러리를 가져 오는 것이 복잡해집니다. 서로 연결되므로 라이브러리 크기가 커집니다. 데이터 구조를 반환하거나 튜플 대신 매개 변수 전달을 사용합니다. 파이썬에서 튜플을 반환 할 때 데이터 구조는 반환 된 값의 순서와 유형이며 명시 적이 지 않습니다.


5

잘 디자인 된 코드에는 일반적으로 필요하지 않기 때문에 거의 볼 수 없습니다. 익명 구조체를 사용하는 것이 명명 된 구조체를 사용하는 것보다 우월한 경우가 많지 않습니다. 모든 튜플이 실제로 나타내는 것은 익명 구조체이므로 대부분의 상황에서 대부분의 코더는 실제 작업을 수행합니다.

튜플 반환이 의미가있는 함수 "f"가 있다고 가정 해 보겠습니다. 일반적으로 이러한 기능은 일반적으로 실패 할 수있을 정도로 복잡합니다.

"f"CAN이 실패하면 상태 리턴이 필요합니다. 결국 호출자가 실패를 감지하기 위해 모든 매개 변수를 검사 할 필요가 없습니다. "f"는 아마도 패턴에 맞을 것입니다 :

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

예쁘지는 않지만 대안이 얼마나 추악한 지보세요. 여전히 상태 값이 필요하지만 코드는 더 이상 읽을 수없고 짧지도 않습니다. 튜플과 함께 1 개의 사본의 비용이 발생하기 때문에 아마도 더 느릴 것입니다.

std::tuple<int, int, bool> f(int x);
int x = 0;
std::tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

또 다른 중요한 단점은 여기에 숨겨져 있습니다. "ReturnInts"를 사용하면 "f"의 인터페이스를 변경하지 않고 "ReturnInts"를 수정하여 "f"의 반환 값을 변경할 수 있습니다. 튜플 솔루션은 중요한 기능을 제공하지 않으므로 라이브러리 코드에 대한 열등한 답변이됩니다.


1
예외는 해당 인터페이스를 훨씬 더 깔끔하게 만듭니다.
데이비드 스톤

공정하게 (그리고 파티에 매우 늦게) 설정 using std::tuple;하고 tuple코드에서 사용 함으로써 가독성을 높일 수 있습니다.
Alrekr 2015-06-11

2
을 사용 tuple하면 코드가 읽기 쉬워지는 것이 아니라 가독성이 높아집니다. 요즘 대부분의 코드에는 매우 많은 수의 기호가 포함되어 있습니다. 보는 std::tuple것만으로도 정확히 무엇인지 눈으로 알 수 있습니다.
Tom Swirly

3

확실히 튜플은 유용 할 수 있지만 언급했듯이 약간의 오버 헤드와 장애물이 있으며 실제로 사용하기 전에 뛰어 넘어야합니다.

프로그램이 여러 값을 반환하거나 여러 값을 교체해야하는 위치를 지속적으로 찾는 경우 튜플 경로를 사용하는 것이 가치가있을 수 있지만, 그렇지 않으면 고전적인 방식으로 작업을 수행하는 것이 더 쉽습니다.

일반적으로 모든 사람이 이미 Boost를 설치 한 것은 아니며, 다운로드하고 튜플 기능을 위해 작동하도록 include 디렉토리를 구성하는 번거 로움을 겪지 않을 것입니다. 이미 Boost를 사용하는 사람들은 Boost가 아닌 사용자보다 프로그램에서 튜플 사용을 찾을 가능성이 더 높고 다른 언어 (Python이 떠오른다)에서 온 이주자들은 단순히 튜플 부족에 대해 화를 낼 가능성이 더 큽니다. 튜플 지원을 추가하는 방법을 탐색하는 것보다 C ++에서.


1

데이터 저장소 std::tuple는 a struct와 어레이 모두에서 최악의 특성을 가지고 있습니다 . 모든 액세스는 n 번째 위치를 기반으로하지만 루프를 tuple사용하여 반복 할 수 없습니다 for.

따라서의 요소 tuple가 개념적으로 배열이면 배열을 사용하고 요소가 개념적으로 배열이 아닌 경우 구조체 (이름이 지정된 요소가있는)가 더 유지 관리하기 쉽습니다. ( a.lastname)보다 설명이 더 많습니다 std::get<1>(a).

이로 인해 OP에서 언급 한 변환이 튜플에 대한 유일한 사용 사례로 남습니다.


0

많은 사람들이 Boost.Tuple 대신 Boost.Any 및 Boost.Variant (일부 엔지니어링 포함)를 사용한다는 느낌이 있습니다.


왜 이런 식으로 효율적인 정적 타이핑을 바꾸겠습니까?
Zifre

Boost.Variant는 완전히 형식이 안전합니다.
user21714

1
죄송합니다, 예, 형식이 안전하지만 런타임 입력을 수행합니다.
Zifre

5
Tuple이 Any / Variant를 어떻게 대체 할 수 있는지 모르겠습니다. 그들은 같은 일을하지 않습니다.
Mankarse 2011

1
@Zifre 저자를 대변 할 수는 없지만 여기에서 의미하는 바는 다른 컨테이너 유형과 함께 사용하는 것입니다.
Tim Seguine 2013 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.