2016-06-06 5 views
0

아래 코드에서 두 개의 비 관련 구조체 B와 C가 포함 된 유니온 유사 클래스 S를 보여줍니다. 비 POD std :: string을 인스턴스화하는 방법을 보여줍니다 그것을 다시 삭제 한 다음 S를 S :: CC로 전환하고 num int를 설정하십시오.std :: initializer_list에서 유니온 같은 클래스 사용

#include <vector> 
#include <string> 
#include <iostream> 
#include <memory> 

struct B 
{ 
    B() {} 
    ~B() {} 
    std::string str; 
    void Func1() {} 
}; 

struct C 
{ 
    C() {} 
    ~C() {} 
    int num; 
    void Func2() {} 
}; 

struct S 
{ 
    S() { tag = CC; } 
    S(const S& s) 
    { 
    switch(s.tag) 
    { 
     case BB: 
     new (&b.str) std::string; 
     b.str = s.b.str; 
     break; 

     case CC: 
     c.num = s.c.num; 

     default: 
     break; 
    } 
    } 

    ~S() 
    { 
    switch(tag) 
    { 
     case BB: 
     b.str.~basic_string<char>(); 
     break; 

     case CC: 
     c.num = 0; 
     break; 

     default: 
     break; 
    } 
    } 

    enum { BB, CC } tag; 
    union 
    { 
    B b; 
    C c; 
    }; 
}; 

struct H 
{ 
    H(std::initializer_list<S> initializerList) : initListVect(initializerList) {} 
    std::vector<S> initListVect; 
}; 

int main() 
{ 
    S s; 
    s.tag = S::BB; 
    new (&s.b.str) std::string; // docs say use new placement to create memory 
    s.b.str = "bbb"; 
    s.b.str.~basic_string<char>(); // string usage in B ok 

    s.tag = S::CC; 
    s.c.num = 333; // int usage in C ok 

    H h { }; // what should the init list be if I wanted 3 list elements S::BB, S::CC, S::BB? 

    return 0; 
} 

그러나 내 목표는 std :: initializer_list에서 S를 사용하는 것입니다. h 형식을 초기화하기 위해 형식을 알아야합니다. 이 S :: BB, S :: CC, S :: BB로 h를 초기화하고 싶다면 인수는 무엇이되어야 하는가?

제 컴파일러는 VS2015입니다.

편집 : 이 게시물의 역사 : 내 게시는 std :: initializer_list에 컴파일 타임 - 추론 가능한 이기종 개체를 저장하는 질문에 대한 명확한 답이 필요합니다. 이 질문은 이전에 여러 번 묻어 왔으며 답변을 많이 시도했습니다 (Heterogeneous containers in C++ 참조). 가장 단순한 대답은 다형성 (polymorphism)을 사용하는 것이지만, 이것은 컴파일 타임 (템플릿)에서 타입을 정의 할 수있는 능력을 무시합니다. 게다가, 이종의 비 관련 객체들은 다형성으로 함께 그룹화되어 파생 된 많은 데이터 멤버들이 쓸모없고, 다운 스트림에서 사용 및 유지 관리 혼란을 초래한다는 것을 의미합니다. 다른 조언은 boost :: any 또는 boost :: variant를 사용하는 것이었지만 이것은 다형성과 같은 약점이 있으며 메시지 선언의 명확성을 줄입니다. 컨테이너 객체의 이질성에 대한 또 다른 시도는 std :: tuple을 사용하는 것이었지만, initializer_list가 확실히 튜플을 포함 할 수 있지만,이 접근법은 컴파일 타임 타입 분해도 무시합니다. 1999 년에 작성된 Heterogeneous, Nested STL Containers in C++이라는 논문이 템플릿 템플릿 인수를 사용하여 이질성 문제를 해결하는 것으로 나타났습니다. 이 모든 후, 나는 여기에 나의 게시로 이끄는 계급 같은 노동 조합에 정착했다. 비 관련/이종 컨테이너 객체에 대한 클래스와 같은 조합은 완벽한 메시지 선언 명확성, 객체 크기 모호성을 가지지 않으며 템플릿을 컴파일 할 수있는 시간을 가지며 우수한 다운 스트림 유지 관리 시나리오를 이끌어냅니다.

편집 2 : (5 주 후) 다음은 일어난 일입니다. 1)이 게시에서 조언을 준 전체 클래스와 유사한 조합 솔루션을 구현했습니다. 결과는 지루하고 다루기 힘들었고 '태그'는 각 하위 기능을 호출 할 하위 방법을 식별하는 데 사용되었습니다. 코드 유지 보수에 관한 낮은 등급. 2) C++ 17이 std :: variant를 승인했습니다. 현재 VS2015 Update 2에서는 아직 구현되지 않았으므로 boost :: variant를 사용하도록 설정했습니다. Visitor 패턴을 사용하여 초기화 된 변형 멤버 및 멤버 함수에 액세스 할 수있게하는 What is the right c++ variant syntax for calling a member function set to a particular variant?을 참조하십시오. 이렇게하면 '태그'스위치와 변형 'get'호출이 제거됩니다. 최종선 : 클래스와 유사한 유니온을 삭제하고 initializer_list를 사용하여 컴파일 타임에 초기화 가능한 모든 멤버 함수를 저장하는 유지 보수 가능한 코드를 만들기위한 변형을 채택했습니다 (읽기 : 유지 관리 용이).

+1

먼저,'S' 복사 생성자가 실제로 작동하도록하십시오. 's.tag '이 무엇인지에 따라 복사 구조에 의해'B' 또는'C'를 생성해야합니다. 또한 소멸자가'tag '에 따라 올바른 타입을 파괴하도록하십시오. 클래스 밖에서 수동으로'new'와 소멸자를 호출하는 것을 피해야합니다. 형식 중 하나를 기본 생성자의 기본 형식으로 지정하십시오. – vu1p3n0x

+0

복사 생성자가 업데이트되었습니다. – rtischer8277

답변

1

좋아, 나는 기분이 좋아 관례화 된 노동 조합을 만들었으므로 너를 세울 수있는 물건이야. 나는 귀하의 S 구조를보다 잘 준수하고 사용할 수 있도록 재 작성했습니다. 당신이 잘못하고 있었 BC의 생성과 소멸을 생략하고 내부 변수 똑바로가는 것들의

struct S 
{ 
    S() : tag(CC) // initializer 
    { 
    new (&c) C; // make C object 
    } 
    S(int num) : tag(CC) // added integer constructor 
    { 
    new (&c) C; 
    c.num = num; 
    } 
    S(const std::string& str) : tag(BB) // added string constructor 
    { 
    new (&b) B; 
    b.str = str; 
    } 
    S(const S& s) : tag(s.tag) 
    { 
    if (tag == CC) 
    { 
     new (&c) C; // construct c 
     c.num = s.c.num; 
    } 
    else if (tag == BB) 
    { 
     new (&b) B; // construct b, not b.str 
     b.str = s.b.str; 
    } 
    } 
    S& operator= (const S& s) // added assignment operator 
    { 
    if (tag == s.tag) // just copy b or c 
    { 
     if (tag == CC) 
     c = s.c; 
     else 
     b = s.b; 
    } 
    else // reconstruct b or c 
    { 
     if (tag == CC) 
     { 
     c.~C(); // destroy c 
     new (&b) B; // construct b 
     b.str = s.b.str; 
     } 
     else 
     { 
     b.~B(); // destroy b 
     new (&c) C; // construct c 
     c.num = s.c.num; 
     } 
     tag = s.tag; 
    } 

    return *this; 
    } 

    ~S() 
    { 
    if (tag == CC) 
    { 
     c.~C(); // destroy c 
    } 
    else if (tag == BB) 
    { 
     b.~B(); // destroy b, not b.str 
    } 
    } 

    enum { BB, CC } tag; 
    union 
    { 
    B b; 
    C c; 
    }; 
}; 

하나 (I은 주석으로 표시 변경했습니다). 사소한 일이 있어도 항상 유형을 적절하게 작성하고 파기해야합니다. 이 문제가 해결 될 수 있지만 이러한 개체를 올바르게 초기화하지 않으면 문제가 발생하기 만합니다 (나중에 B 또는 C을 변경하는 것이 더 쉽습니다).

클래스를 더 쉽게 사용하려면 할당 연산자와 함께 std::stringint에 대한 올바른 생성자를 추가했습니다.지금 우리는 우리가 원하는 방법 개체를 만들 수 있기 때문에, 당신의 main()는 다음과 같이 수 :

int main() 
{ 
    S s;     // default S 
    s = std::string("bbb"); // set to string 
    s = 333;    // set to number 

    // use initialization list 
    H h { std::string("bb"), 33, std::string("bb") }; 

    return 0; 
} 

차라리 S에 의존하지 않고 자신의 내부를 구축하는 생성자를 사용하는 BC을 수정하는 것이 좋습니다.