2010-06-17 2 views
8

Мне интересно, есть ли способ установить ограничения на класс шаблона?
Укажите, что каждый тип, замещенный в шаблоне, должен иметь определенного предка (реализовать некоторый интерфейс).Ограничения на шаблон C++

template < class B > //and every B must be a child of abstract C 
class A { 
public: 
    B * obj; 
    int f() { 
     return B::x + this->obj->f(); 
    } 
}; 

Как => в Haskell

func :: (Ord a, Show b) => a -> b -> c 
+1

Почему вы думаете, что это нужно? – fredoverflow

+0

Возможный дубликат [C++ класс шаблона определенного базового слова] (http://stackoverflow.com/questions/2012950/c-class-template-of-specific-baseclass) – SLaks

+1

sooooo не начинающий вопрос :-). Концепции на самом деле довольно спорные. –

ответ

2

Вы можете use BOOST_STATIC_ASSERT или подобная библиотека отстаивать свои ограничения на параметр шаблона.

Например:

#include <limits> 
#include <boost/static_assert.hpp> 

template <class UnsignedInt> 
class myclass 
{ 
private: 
    BOOST_STATIC_ASSERT((std::numeric_limits<UnsignedInt>::digits >= 16) 
         && std::numeric_limits<UnsignedInt>::is_specialized 
         && std::numeric_limits<UnsignedInt>::is_integer 
         && !std::numeric_limits<UnsignedInt>::is_signed); 
public: 
    /* details here */ 
}; 

EDIT: Для примера, вы можете написать

template < class B > 
class A { 
    BOOST_STATIC_ASSERT(boost::is_base_of<C, B>); 

public: 
    B * obj; 
    int f() { 
     return B::x + this->obj->f(); 
    } 
}; 
2

Вы можете использовать трюк, как это (если вы не хотите использовать Boost) :

class Base 
    { 
    public: 
     static const int TEMPLATE_REQUIRES_BASE_CLASS = 0; 
    }; 

class Correct : public Base 
    { 
    }; 

class Incorrect 
    { 
    }; 

template <typename T> 
class TMPL 
    { 
    static const int TEMPLATE_REQUIRES_BASE_CLASS = T::TEMPLATE_REQUIRES_BASE_CLASS; 
    T *m_t; 
    }; 

void main() 
{ 
TMPL<Correct> one;  // OK 
TMPL<Incorrect> two; // Will not compile 
} 

Первая строка будет скомпилирована. Второй не будет компилировать и выдаст следующее сообщение об ошибке:

test.cpp 
test.cpp(18) : error C2039: 'TEMPLATE_REQUIRES_BASE_CLASS' : is not a member of 'Incorrect' 
     test.cpp(12) : see declaration of 'Incorrect' 
     test.cpp(25) : see reference to class template instantiation 'TMPL<T>' being compiled 
     with 
     [ 
      T=Incorrect 
     ] 
test.cpp(18) : error C2065: 'TEMPLATE_REQUIRES_BASE_CLASS' : undeclared identifier 
test.cpp(18) : error C2057: expected constant expression 
+0

Этот код не подпадает под g ++ 4.4 из-за неизвестной причины.В общем, я бы сказал, что в этом случае обычно используется перечисление: enum {TEMPLATE_REQUIRES_BASE_CLASS = 0}; У этого есть дополнительное преимущество неудачи, как ожидалось в g ++. – iksemyonov

+0

@Semen, GCC не соответствует требованиям. Стандарт требует, чтобы объявление элемента данных создавалось, когда класс неявно создается экземпляр, а инициализатор в классе является частью декларации, а не его определением. Однако стандарт несколько неясен, говоря: «Инициализация (и любые связанные с ней побочные эффекты) статического члена данных не происходит, если сам статический член данных не используется таким образом, который требует определения статического члена данных. " - это правило, однако, похоже, применимо только к инициализатору вне класса (не имеет смысла иначе) –

+0

@Johannes: спасибо за объяснение, я думал, что схожу с ума, когда он построил отлично ... но я думаю, перечисление все еще проще в использовании. (Я просто следую Vandervourde :) – iksemyonov

6

Будущая версия C++ будет поддерживать это изначально с использованием понятий (которые не делают его в C++ 11).

Один из способов подойти к решению проблемы заключается в использовании специализации на фиктивном параметре шаблона:

class C {}; 
template <class B, class dummy=void> 
class A; 

template <class B> 
class A<B, typename enable_if<is_base_and_derived<C, B> >::type> 
{ 
    // class definition here 
}; 

struct D : C {}; 

A<D> d;  // fine 
A<int> n; // compile error - undefined class A<B> 

Я поставил автономные определения из enable_if и is_base_and_derivedhere.

+0

Спасибо, удивительный кусок кода – Andrew

1

Шаблоны - это своего рода утка, набирая на C++.

Если ваш класс поддерживает все, что использует шаблон, то он может использоваться как аргумент шаблона, иначе он не может.

Если в шаблоне у вас есть что-то вроде

C *instance; 

void foo(T *t) 
{ 
    instance = t; 
} 

тогда вы исполнившие, что T является производным от C (или, по крайней мере, совместимых-присваиваний для указателей)

3

Следующих работ в VC10 с использованием static_assert. Я только что видел, что это использовалось и на самом деле не выкопало многое в том, что делает static_assert - возможно, кто-то может ответить на это.

#include <type_traits> 

class Base 
{ 

}; 

class Derived : public Base 
{ 

}; 

class SomeRandomClass 
{ 

}; 

template<typename T> 
class A 
{ 
    static_assert(std::tr1::is_base_of<Base, T>::value, "T not derived from Base"); 
}; 



int _tmain(int argc, _TCHAR* argv[]) 
{ 
    argc; argv; 

    // 
    // This will compile 
    A<Derived> a; 

    // 
    // This will throw a compilation error 
    A<SomeRandomClass> b; 

    return 0; 
} 

Выход компилятора:

1>d:\temp\aaa\aaa\aaa.cpp(25): error C2338: T not derived from Base 
1>   d:\temp\aaa\aaa\aaa.cpp(41) : see reference to class template instantiation 'A<T>' being compiled 
1>   with 
1>   [ 
1>    T=SomeRandomClass 
1>   ] 
Смежные вопросы