C ++ 프로젝트에서 "Tagged Union"또는 Variant 유형의 많은 오픈 소스 C ++ 11 구현 boost::variant
또는 많은 오픈 소스 C ++ 11 구현 중 하나를 사용하십니까?
boost::variant
훌륭한 라이브러리입니다. 내가 마음에 들지 않는 boost::variant
에 대한 몇 가지 사항을 해결하기 위해 strict_variant
만들었습니다.
tl; dr 버전은 boost::variant
또는 std::variant
와 달리, strict_variant
동작을 던지는 지원 유형의 노력에 예외를 던지지 않거나 동적 할당을 만들지 않는다는 것입니다. 기본 버전은 이런 일이 발생하면 정적 인제에 실패합니다. strict_variant::easy_variant
이 상황에서 할당을 할 것이므로 원하는 경우 그 두 가지 버전의 변형을 "멋지게 재생"할 수 있습니다. 이런 종류의 것은 종종 실시간 요구 사항이있는 프로젝트 나 포함 된 장치에서 주요 관심사입니다. boost::variant
에서 제공하는 사용 편의성을 원하는 "기존"프로젝트에서 사용될 수있는 라이브러리를 만드는 경우, 제한적인 요구 사항이있는 프로젝트에서도 사용될 수 있으며 variant
유형을 사용하려는 경우 API의 일환으로 strict_variant
모든 사람을 행복하게 할 수있는 방법을 제공 할 수 있습니다.
이 외에도 변형 인터페이스에 해결 된 몇 가지 문제가 있으며 일상적인 IMHO를 사용하는 것이 더 즐겁습니다. (이것은 실제로 프로젝트의 원래 동기였습니다.)
경고 나 오류 메시지없이 컴파일 할 수있는이 코드가 마음에 들지 않았습니다.
boost::variant<std::string, int > v;
v = true ;
나는 보통 내 variant
어떤 암시 적 변환이 일어날 수 있는지에 대해 더 제한적이라고 생각합니다.
나는 과부하 해상도가 모호하더라도 이와 같은 것들이 컴파일하고 의미가있는 것을하기를 원했습니다 .
variant< bool , long , double , std::string> v;
v = true ; // selects bool
v = 10 ; // selects long
v = 20 . 5f ; // selects double
v = " foo " ; // selects string
또한 그러한 행동 (그러한 경우에 선택되는 것)이 휴대하기를 원했습니다.
( boost::variant
불행한 행동을하는 이와 같은 코드 예제의 경우, 문서의 "추상 및 동기 부여"를 참조하십시오.)
strict_variant
에서는 일부 후보를 제거하여 이러한 상황에서 과부하 해상도를 수정합니다.
예를 들어:
bool
, Integral, Floating Point, Pointer, 캐릭터 및 기타 클래스 간의 표준 변환을 금지합니다.int -> long
int -> long long
후보자라면 long long
제거됩니다.자세한 내용은 문서를 참조하십시오.
나는 그 boost::variant
내 객체의 백업 사본을 조용히 만들 것이다. 예를 들어, A
와 B
모든 CTOR 및 DTOR 호출을 기록하도록 정의 된이 간단한 프로그램을 고려하십시오.
int main () {
using var_t = boost::variant<A, B>;
var_t v{ A ()};
std::cout << " 1 " << std::endl;
v = B ();
std::cout << " 2 " << std::endl;
v = A ();
std::cout << " 3 " << std::endl;
}
boost::variant
다음 출력을 생성합니다.
A ()
A(A&&)
~A()
1
B()
B(B&&)
A( const A &)
~A()
B( const B &)
~A()
~B()
~B()
2
A()
A(A&&)
B( const B &)
~B()
A( const A &)
~B()
~A()
~A()
3
~A()
이것은 일부 프로그래머에게는 매우 놀라운 일입니다.
대조적으로, C ++ 17 std::variant
또는 "때때로"시맨틱스가있는 변형 중 하나를 사용하면 이와 같은 것을 얻을 수 있습니다 ( std::experimental::variant
).
A ()
A(A&&)
~A()
1
B()
~A()
B(B&&)
~B()
2
A()
~B()
A(A&&)
~A()
3
~A()
이것은 boost::variant
의 내부 세부 사항에 대해 모르는 순진한 프로그래머가 기대하는 것과 훨씬 더 가깝습니다.
이런 종류의 것은 일반적으로 중요하지 않지만 때로는 불쾌한 메모리 손상 문제를 디버깅하는 경우 (아마도 변형에 포함 된 개체 중 하나에 나쁜 코드가있을 수 있음) 추가 객체, 움직임 및 사본이있을 수 있습니다. 우연히 더 복잡하게 만듭니다.
strict_variant
로 얻는 것은 다음과 같습니다.
A ()
A(A&&)
~A()
1
B()
B(B&&)
~A()
~B()
2
A()
A(A&&)
~B()
~A()
3
~A()
그러나 strict_variant
빈 상태가 없으며 완전히 예외입니다!
( gcc 5.4
의이 예제는 example
폴더의 코드를 참조하십시오.)
차이점을 요약하기 위해 :
std::variant
거의 비어 있고 항상 스택 기반입니다. 사실, 예외가 발생하면 정확히 비어 있습니다. 나중에 비어있을 때 방문하려고하면 다른 예외가 발생합니다.boost::variant
결코 치어지지 않으며 일반적으로 스택 기반입니다. 예외가 발생할 수 있을 때마다 동적 할당과 백업 사본을 만들어야하지만 실제로 예외가 발생하지 않으면 해방됩니다.strict_variant
는 결코 비어 있지 않으며, 현재 값 유형이 움직이지 않을 때 정확히 스택 기반. 백업 이동이나 복사를하지 않으며 예외는 절대 발생하지 않습니다. 각 접근 방식에는 장점이 있습니다. Strict_variant 접근 방식을 더 간단하게 발견하고 boost::variant
및 std::variant
의 단점을 피하기 때문에 strict_variant
접근 방식을 선택했습니다. 그리고 만약 당신이 당신의 모든 유형이 당신의 모든 유형을 건설 할 수있게한다면, 내가 자주 찾을 수있는 경우, strict_variant
빈 상태없이 std::variant
과 동일한 최적의 성능을 제공합니다.
디자인에 대한 심도있는 토론을 보려면 문서를 확인하십시오.
변형에 대한 부드러운 소개와 엄격한 변수에 대한 개요는 다음과 같은 대화의 슬라이드를 참조하십시오 : [pptx] [pdf]
Github 페이지에서.
strict_variant
c ++ 11 표준을 대상으로합니다.
gcc >= 4.8
및 clang >= 3.5
에서 작동하는 것으로 알려져 있으며 MSVC 2015
에 대해 테스트됩니다.
strict_variant
-fno-exceptions
및 -fno-rtti
가 필요한 프로젝트에서 as-as-as-iS를 사용할 수 있습니다.
Boost 소프트웨어 라이센스에 따라 엄격한 변형을 사용할 수 있습니다.