2010-06-17 28 views
8

Tôi tự hỏi có cách nào để đặt hạn chế đối với lớp mẫu không?
Chỉ định rằng mọi loại được thay thế trong mẫu phải có tổ tiên cụ thể (nhận ra một số giao diện).Hạn chế mẫu 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(); 
    } 
}; 

Giống như => trong Haskell

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

Tại sao bạn nghĩ bạn cần điều này? – fredoverflow

+0

có thể trùng lặp của [mẫu lớp C++ của baseclass cụ thể] (http://stackoverflow.com/questions/2012950/c-class-template-of-specific-baseclass) – SLaks

+1

sooooo không phải là câu hỏi mới bắt đầu :-). Các khái niệm thực sự khá gây tranh cãi. –

Trả lời

2

Bạn có thể use BOOST_STATIC_ASSERT hoặc một thư viện tương tự để khẳng định giới hạn của bạn trên mẫu tham số.

Ví dụ:

#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: Ví dụ, bạn có thể viết

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

Bạn có thể sử dụng một thủ thuật như thế này (nếu bạn không muốn sử dụng 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 
} 

Dòng đầu tiên sẽ biên dịch. Thông tin thứ hai sẽ không biên dịch và sẽ cung cấp cho lỗi sau:

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

Mã này không bị lỗi theo g ++ 4.4, do một số lý do không xác định.Nói chung, tôi muốn nói rằng trong trường hợp này, một enum thường được sử dụng: enum {TEMPLATE_REQUIRES_BASE_CLASS = 0}; Điều này có lợi ích bổ sung là không thành công như mong đợi trong g ++. – iksemyonov

+0

@Semen, GCC không phù hợp. Tiêu chuẩn yêu cầu khai báo thành viên dữ liệu được khởi tạo khi lớp được khởi tạo ngầm, và bộ khởi tạo trong lớp là một phần của khai báo thay vì định nghĩa của nó. Tuy nhiên, tiêu chuẩn không rõ ràng, nói rằng "khởi tạo (và bất kỳ tác dụng phụ liên quan) nào của thành viên dữ liệu tĩnh không xảy ra trừ khi thành viên dữ liệu tĩnh được sử dụng theo cách yêu cầu định nghĩa thành viên dữ liệu tĩnh tồn tại. " - Tuy nhiên, quy tắc này dường như chỉ áp dụng cho trình khởi chạy ngoài lớp (không có ý nghĩa khác) –

+0

@Johannes: cảm ơn lời giải thích, tôi nghĩ rằng tôi đã phát điên khi nó được xây dựng tốt ... nhưng tôi đoán enum vẫn là đơn giản hơn để sử dụng. (Tôi chỉ đang theo dõi Vandervourde :) – iksemyonov

6

Phiên bản tương lai của C++ sẽ hỗ trợ các khái niệm nguyên gốc này (không đưa vào C++ 11).

Một cách để tiếp cận vấn đề là sử dụng chuyên môn về một tham số mẫu giả:

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> 

Tôi đã đặt định nghĩa độc lập của enable_ifis_base_and_derivedhere.

+0

Cảm ơn, đoạn mã tuyệt vời – Andrew

1

Mẫu là loại nhập vịt trong C++.

Nếu lớp học của bạn hỗ trợ mọi thứ mà mẫu sử dụng, thì nó có thể được sử dụng làm đối số mẫu, nếu không thì nó không thể.

Nếu trong mẫu của bạn, bạn có cái gì đó như

C *instance; 

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

sau đó bạn đang thực thi mà T có nguồn gốc từ C (hoặc ít nhất assignement tương thích cho con trỏ)

3

Các tác phẩm sau đây trong VC10 sử dụng static_assert. Tôi đã nhìn thấy điều này được sử dụng và chưa thực sự đào sâu vào những gì static_assert thực sự làm - có lẽ ai đó khác có thể trả lời điều đó.

#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; 
} 

Trình biên dịch ra hạnh phúc:

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>   ]