C ++ 템플릿 매개 변수를 하위 클래스로 제한


80

템플릿 매개 변수 T를 특정 클래스의 하위 클래스로 지정하려면 Baseclass어떻게해야합니까? 이 같은:

template <class T : Baseclass> void function(){
    T *object = new T();

}

3
이를 통해 무엇을 성취하려고합니까?
sth

2
T가 실제로 하위 클래스 또는 클래스 자체의 인스턴스인지 확인하고 싶습니다. 내가 제공 한 함수 내부의 코드는 거의 관련이 없습니다.
phant0m 2010

6
반대로 매우 관련이 있습니다. 테스트에 작업을 넣는 것이 좋은 생각인지 아닌지를 결정합니다. 많은 경우 (모두?)에서 이러한 제한을 직접 적용 할 필요가 전혀 없으며 인스턴스화 할 때 컴파일러가 수행하도록합니다. 예를 들어, 수락 된 답변의 경우 T에서 파생 되었는지 여부를 확인하는 것이 좋습니다 Baseclass. 현재이 검사는 암시 적이며 과부하 해결에 표시되지 않습니다. 그러나 그러한 암시 적 제약이 어디에도 수행되지 않으면 인위적인 제한의 이유가없는 것으로 보입니다.
Johannes Schaub-litb

1
그래, 난 동의. 그러나 나는 이것을 성취하는 방법이 있는지 알고 싶었습니다. :) 물론, 당신은 매우 유효한 지적을 가지고 있으며 통찰력에 감사드립니다.
phant0m 2010

답변:


53

이 경우 다음을 수행 할 수 있습니다.

template <class T> void function(){
    Baseclass *object = new T();

}

T가 Baseclass의 서브 클래스가 아니면 (또는 T Baseclass) 컴파일되지 않습니다 .


아 그래, 그거 좋은 생각이야. 감사! 그러면 템플릿 정의에서 정의 할 방법이 없습니까?
phant0m 2010

2
@ phant0m : 맞습니다. 템플릿 매개 변수를 명시 적으로 제한 할 수 없습니다 (c ++ 0x에 대해 고려되었지만 삭제 된 개념 사용 제외). 모든 제약은 사용자가 수행하는 작업에 의해 암시 적으로 발생합니다 (즉, 유일한 제약은 "유형이 수행되는 모든 작업을 지원해야합니다").
sepp2k

1
아 ic. 설명해 주셔서 감사합니다!
phant0m 2010

8
T () 생성자를 실행하고 T () 생성자가 있어야합니다. 이러한 요구 사항을 피하는 방법은 내 대답을 참조하십시오.
Douglas Leeder

3
훌륭하고 명확하지만 T가 "무거운"클래스 인 경우 문제가됩니다.
3Dave 2014 년

84

C ++ 11 호환 컴파일러를 사용하면 다음과 같이 할 수 있습니다.

template<class Derived> class MyClass {

    MyClass() {
        // Compile-time sanity check
        static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");

        // Do other construction related stuff...
        ...
   }
}

CYGWIN 환경에서 gcc 4.8.1 컴파일러를 사용하여 이것을 테스트 했으므로 * nix 환경에서도 작동해야합니다.


저에게는 다음과 같이 작동합니다. template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");...
Matthias Dieter Wallnöfer

1
런타임에 추가 코드를 피하는 가장 읽기 쉬운 답변이라고 생각합니다.
Kyle

50

런타임에 덜 쓸모없는 코드를 실행하려면 다음을 참조하십시오 . http://www.stroustrup.com/bs_faq2.html#constraints 는 컴파일 시간 테스트를 효율적으로 수행하고 더 좋은 오류 메시지를 생성하는 일부 클래스를 제공합니다.

특히:

template<class T, class B> struct Derived_from {
        static void constraints(T* p) { B* pb = p; }
        Derived_from() { void(*p)(T*) = constraints; }
};

template<class T> void function() {
    Derived_from<T,Baseclass>();
}

2
저에게는 이것이 가장 좋고 가장 흥미로운 답변입니다. 이와 유사하게 적용 할 수있는 모든 종류의 제약 조건에 대해 자세히 알아 보려면 Stroustrup의 FAQ를 확인하십시오.
Jean-Philippe Pellet 2013 년

1
실제로 이것은 지옥의 대답입니다! 감사. 언급 된 사이트는 여기로 이동되었습니다. stroustrup.com/bs_faq2.html#constraints
Jan Korous 2013

이것은 훌륭한 대답입니다. 경고 피하기 위해 어떤 좋은 방법이 있습니다 unused variable 'p'와는 unused variable 'pb'?
Filip S.

@FilipS. (void)pb;뒤에 추가하십시오 B* pb = p;.
bit2shift

11

개념은 필요하지 않지만 SFINAE를 사용할 수 있습니다.

template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

이는 조건이 충족 될 때만 함수를 인스턴스화하지만 조건이 충족되지 않으면 현명한 오류를 제공하지 않습니다.


이와 같은 모든 기능을 래핑하면 어떨까요? btw 무엇을 반환합니까?
the_drow 2010-07-04

enable_if해당 디폴트 매개 변수를 두 번째 형식을 사용합니다 void. 표현식 enable_if< true, int >::type은 유형을 나타냅니다 int. 첫 번째 질문이 무엇인지 정말 이해할 수 없습니다. SFINAE를 원하는대로 사용할 수 있지만 모든 기능에 대해 이것으로 무엇을 하려는지 잘 이해하지 못합니다.
David Rodríguez-dribeas 2010-07-04

7

C ++ 11부터 Boost 또는 static_assert. C ++ 11은 is_base_ofenable_if. C ++ 14에는 편의 유형이 도입 enable_if_t되었지만 C ++ 11을 사용 enable_if::type하는 경우 간단히 대신 사용할 수 있습니다 .

대안 1

David Rodríguez 의 솔루션은 다음과 같이 다시 작성할 수 있습니다.

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of<Base, T>::value, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

대안 2

C ++ 17부터 is_base_of_v. 솔루션은 다음과 같이 다시 작성할 수 있습니다.

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of_v<Base, T>, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

대안 3

전체 템플릿을 제한 할 수도 있습니다. 이 방법을 사용하여 전체 클래스를 정의 할 수 있습니다. 의 두 번째 매개 변수가 어떻게 enable_if_t제거되었는지 확인하십시오 (이전에 void로 설정 됨). 기본값은 실제로 void이지만 사용하지 않기 때문에 중요하지 않습니다.

#include <type_traits>

using namespace std;

template <typename T,
          typename = enable_if_t<is_base_of_v<Base, T>>>
void function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

로부터 문서 템플릿 매개 변수의, 우리는 그 볼 typename = enable_if_t...빈 이름을 가진 템플릿 매개 변수입니다. 우리는 단순히 타입의 정의가 존재하는지 확인하기 위해 그것을 사용하고 있습니다. 특히 는의 밑이 아닌 enable_if_t경우 정의 Base되지 않습니다 T.

위의 기술은 enable_if.


다음과 같이 대안 3을 작성할 수 있다면 좋지 않을까요? template <class T : Base>
Macsinus

4

당신이 사용할 수있는 부스트 개념 확인 '들 BOOST_CONCEPT_REQUIRES:

#include <boost/concept_check.hpp>
#include <boost/concept/requires.hpp>

template <class T>
BOOST_CONCEPT_REQUIRES(
    ((boost::Convertible<T, BaseClass>)),
(void)) function()
{
    //...
}

0

기본 클래스에있는 템플릿 내부의 함수를 호출합니다.

이 함수에 액세스 할 수없는 유형으로 템플릿을 인스턴스화하려고하면 컴파일 타임 오류가 발생합니다.


3
이것은 그 보장하지 않는 T A는 BaseClass 에서 선언 된 회원이 때문에 BaseClass의 선언에 반복 될 수있다 T.
Daniel Trebbien 2010
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.