2010-03-12 53 views
12

Giả sử chúng ta có một mẫu chức năng "foo":Một mẫu chuyên môn cho nhiều lớp

template<class T> 
void foo(T arg) 
{ ... } 

tôi có thể làm chuyên môn đối với một số loại đặc biệt, ví dụ

template<> 
void foo(int arg) 
{ ... } 

Nếu tôi muốn sử dụng chuyên môn tương tự cho tất cả các loại BUILTIN số (int, float, double vv) Tôi sẽ viết những dòng nhiều lần. Tôi biết rằng cơ thể có thể được ném ra một chức năng khác và chỉ cần gọi điều này là được thực hiện trong mọi cơ thể của chuyên môn, tuy nhiên nó sẽ đẹp hơn nếu tôi có thể tránh viết "void foo (..." cho mọi loại. bất kỳ khả năng nói với trình biên dịch mà tôi muốn sử dụng chuyên môn hóa này cho tất cả các loại này?

+3

Mục tiêu cuối cùng của bạn là gì? –

+0

Đây không phải là câu trả lời trực tiếp nhưng nó bao gồm chuyên môn hóa so với quá tải và có thể bạn quan tâm: http://www.gotw.ca/publications/mill17.htm – sellibitze

Trả lời

19

Bạn có thể sử dụng std::numeric_limits để xem liệu loại đó có phải là loại số (is_specialized là đúng cho tất cả các loại cơ bản dạng số nguyên và số nguyên) không.

// small utility 
template<bool> struct bool2type { }; 

// numeric 
template<typename T> 
void fooImpl(T arg, bool2type<true>) { 

} 

// not numeric 
template<typename T> 
void fooImpl(T arg, bool2type<false>) { 

} 

template<class T> 
void foo(T arg) 
{ fooImpl(arg, bool2type<std::numeric_limits<T>::is_specialized>()); } 
+0

Ý tưởng gọn gàng! Nhưng tại sao không chỉ làm cho 'fooImpl' phụ thuộc vào một kiểu và một đối số boolean? – Vlad

+1

@Vlad vì chúng tôi muốn phân nhánh thời gian biên dịch. Nếu chúng ta chuyển một đối số hàm 'bool', chúng ta sẽ có mã được thực thi trong cùng một hàm và sử dụng' if' cho nhánh. Điều đó sẽ không may gõ-kiểm tra cả hai chi nhánh, và rất có thể bỏ lỡ mục tiêu. –

+0

@Johannes: Tôi có nghĩa là hai đối số mẫu, chỉ để dự phòng 'định nghĩa bool2type'. – Vlad

0

có lẽ bạn có thể định nghĩa một mẫu mặc định chức năng sẽ làm việc trên tất cả các loại bản địa, và ủy thác chuyên môn hóa của loại hình tùy chỉnh để sử dụng

3

Bạn có thể sử dụng cách tiếp cận với bộ tiền xử lý.

foo.inc:

template<> 
void foo(TYPE arg) 
{ /* do something for int, double, etc. */ } 

foo.h:

template<class T> 
void foo(T arg) 
{ /*do something */ } 

#define TYPE int 
#include "foo.inc" 
#undef TYPE 

#define TYPE double 
#include "foo.inc" 
#undef TYPE 

, vv

3

Với boost:

#include <boost/type_traits/is_scalar.hpp> 
#include <iostream> 
#include <string> 

namespace detail 
{ 
    typedef const boost::true_type& true_tag; 
    typedef const boost::false_type& false_tag; 

    template <typename T> 
    void foo(const T& pX, true_tag) 
    { 
     std::cout << "special: " << pX << std::endl; 
    } 

    template <typename T> 
    void foo(const T& pX, false_tag) 
    { 
     std::cout << "generic: " << pX << std::endl; 
    } 
} 

template <typename T> 
void foo(const T& pX) 
{ 
    detail::foo(pX, boost::is_scalar<T>()); 
} 

int main() 
{ 
    std::string s = ":D"; 
    foo(s); 
    foo(5); 
} 

Bạn có thể chủ yếu là dễ dàng làm điều đó mà không cần tăng :

#include <iostream> 
#include <string> 

// boolean stuff 
template <bool B> 
struct bool_type {}; 

typedef bool_type<true> true_type; 
typedef bool_type<false> false_type; 

// trait stuff 
template <typename T> 
struct is_scalar : false_type 
{ 
    static const bool value = false; 
}; 

#define IS_SCALAR(x) template <> \ 
      struct is_scalar<x> : true_type \ 
      { \ 
       static const bool value = true; \ 
      }; 

IS_SCALAR(int) 
IS_SCALAR(unsigned) 
IS_SCALAR(float) 
IS_SCALAR(double) 
// and so on 

namespace detail 
{ 
    typedef const true_type& true_tag; 
    typedef const false_type& false_tag; 

    template <typename T> 
    void foo(const T& pX, true_tag) 
    { 
     std::cout << "special: " << pX << std::endl; 
    } 

    template <typename T> 
    void foo(const T& pX, false_tag) 
    { 
     std::cout << "generic: " << pX << std::endl; 
    } 
} 

template <typename T> 
void foo(const T& pX) 
{ 
    detail::foo(pX, is_scalar<T>()); 
} 

int main() 
{ 
    std::string s = ":D"; 
    foo(s); 
    foo(5); 
} 
0

Bạn có thể viết một tập lệnh nhỏ (ví dụ: Perl) để tạo tệp nguồn cho bạn. Tạo một mảng chứa tất cả các kiểu mà bạn muốn chuyên, và nó có viết ra tiêu đề hàm cho mỗi loại. Bạn thậm chí có thể nhúng thực thi tập lệnh trong makefile của bạn để tự động chạy lại nó nếu bạn thay đổi một cái gì đó.

Lưu ý: điều này giả định việc triển khai foo có thể được thực hiện tầm thường và tương tự cho từng loại, ví dụ: chỉ cần gọi đến chức năng triển khai thực. Nhưng nó tránh một loạt các mẫu/tiền xử lý mumbo-jumbo mà có thể làm cho một người bảo trì trong tương lai gãi đầu.

Các vấn đề liên quan