2017-11-28 15 views
0

두 템플릿으로 제공되는 템플릿 클래스 Foo이 있습니다. 템플릿 템플릿 매개 변수는 두 가지가 있습니다. 클래스에 멤버 변수를 저장하고 getter 및 setter를 제공하기 위해 클래스에 몇 가지 추가 기능이 필요합니다. 또한 추가 유형에 대해 추가 static_assert()을 수행해야하며 일반 생성자를 비활성화하고 해당 유형의 매개 변수도 제공해야합니다. 다음과 같이 보입니다 :상속 또는 클래스 특수화를 사용하지 않고 멤버 함수 및 변수를 사용하지 않습니다.

template <class Irrelevant, class Extra> 
class Foo { 
    //other stuff... 
    static_assert(is_good_v<Extra>); 
    Extra extra; 
public: 
    //other stuff... 
    Foo(Irrelevant i) {/* irrelevant */} 

    //now methods I need to enable only when Extra is present 
    Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */} 
    void setExtra(Extra e) {extra = e;} 
    void getExtra() {return extra;} 
}; 

나는 여러 가지 접근법을 시도했지만 아무 것도 만족스럽지 못했습니다. 우선 상속 사용하려고 :

template <class Irrelevant> 
class Bar { 
    //other stuff... 
public: 
    //other stuff... 
    Foo(Irrelevant i) {/* irrelevant */} 
}; 

template <class Irrelevant, class Extra> 
class Foo : public Bar<Irrelevant> { 
    static_assert(is_good_v<Extra>); 
    Extra extra; 
public: 
    Foo(Irrelevant i, Extra e) : Bar<Irrelevant>(i), extra(e) {/* irrelevant */} 
    void setExtra(Extra e) {extra = e;} 
    void getExtra() {return extra;} 
}; 

template <class Irrelevant> 
class Foo : public Bar<Irrelevant> {}; 

이 (유사하거나 뭔가 이상)이 혼자 일을하지만, 다른 곳에서 그들에 템플릿 검사를 수행 할 때 별도의 해결을 요구, 또한 '는 불필요 글로벌 네임 스페이스를 산재 나는 shouldn 그렇게하는거야. 두 번째 방법은 다음과 같이, Foo의 전문성을했다 :

template <class Irrelevant, class... T> 
class Foo {}; 

template <class Irrelevant, class Extra> 
class Foo<Irrelevant, Extra> { 
    //other stuff... 
    static_assert(is_good_v<Extra>); 
    Extra extra; 
public: 
    //other stuff... 
    Foo(Irrelevant i) {/* irrelevant */} 

    //now methods I need to enable only when Extra is present 
    Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */} 
    void setExtra(Extra e) {extra = e;} 
    void getExtra() {return extra;} 
}; 


template <class Irrelevant> 
class Foo<Irrelevant> { 
    //other stuff... 
public: 
    //other stuff... 
    Foo(Irrelevant i) {/* irrelevant */} 
}; 

그리고 이것은 또한 일을하고, 쓰레기 글로벌 네임 스페이스를하지 않았다, 그러나 //other stuff 모든 위대한하지 않은, 중복되었다. 시도하고 싶었던 세 번째 접근법은 std::enable_if 모든 것들 이었지만 작동시키지 못했습니다. 나는이 같은 시도 :

template <class Irrelevant, class Extra = void> 
class Foo { 
    //other stuff... 
    static constexpr bool hasExtra = std::is_same_v<Extra, void>; 
    static_assert(!hasExtra || is_good_v<Extra>); 
    std::conditional<hasExtra, Extra, bool> extra; 
public: 
    //other stuff... 
    template <typename = typename std::enable_if_t<!hasExtra>> 
    Foo(Irrelevant i) {/* irrelevant */} 

    //now methods I need to enable only when Extra is present 
    template <typename = typename std::enable_if_t<hasExtra>> 
    Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */} 

    template <typename = typename std::enable_if_t<hasExtra>> 
    void setExtra(Extra e) {extra = e;} 

    template <typename = typename std::enable_if_t<hasExtra>> 
    void getExtra() {return extra;} 
}; 

을하지만 SFINAE은 그것의 방법을 SFINAE하는 다른 기능이 없기 때문에이 getter 및 setter를 컴파일 할 때에 컴파일 오류가 발생하기 때문에이 분명 작동하지 않습니다. 그리고 Extra == void에 대한 더미 함수를 추가하면 누군가 해당 함수를 사용하는 코드가 컴파일됩니다. 어떻게 제대로 할 수 있습니까?

+0

같은 것을 당신이 _mixins_ 오히려 [CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)를 사용이 필요합니다. – user0042

+0

필자는 쓴대로 Foo를 제외한 모든 이름을 전역 이름 공간에 추가하지 않기를 원합니다. – Joald

+0

글로벌 네임 스페이스에서 믹스 인 클래스를 어떻게 강제 적용 할 수 있습니까? 오직 간접적 인 계층과'enable_if()'를 필요에 따라 추가하십시오. – user0042

답변

3

여분의 경우에만 Foo을 입력하고 부분 전문화를 상속 한 경우, 즉 Foo<Irrelevant, void>, 공통 부분은 무엇입니까?

는 말은 :

// with extra only 
template <typename Irrelevant, typename Extra> 
class Foo : public Foo<Irrelevant, void> 
{ 
    private: 
     Extra extra; 

     static_assert(is_good_v<Extra>); 

    public: 
     // to make visible the inherited constructor(s) 
     using Foo<Irrelevant, void>::Foo; 

     // to disable (?) a specific inherited constructor 
     // Foo(Irrelevant i) = delete; 

     //now methods I need to enable only when Extra is present 
     Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */} 

     void setExtra(Extra e) {extra = e;} 

     Extra getExtra() {return extra;} 
}; 

// common part, no extra 
template <typename Irrelevant> 
class Foo<Irrelevant, void> 
{ 
    public: 
     Foo(Irrelevant i) {/* irrelevant */} 
}; 
+0

정말 멋지 네요. 템플릿 전문화가 같은 템플릿의 다른 전문 분야에서 상속받을 수 있다는 것을 몰랐습니다. 감사합니다! – Joald

+0

@Joald - 그래서 I : 며칠 전에 발견했는데 지금은 광범위하게 사용하고 있습니다. – max66