2012-01-05 31 views
7

Đây chỉ là một câu hỏi về phong cách: Tôi không thích cách của C++ cho mẫu lập trình meta yêu cầu bạn sử dụng kiểu trả về hoặc thêm một đối số giả cho các thủ thuật với SFINAE . Vì vậy, ý tưởng tôi đã đưa ra là đặt điều SFINAE trong các đối số mẫu định nghĩa riêng của mình, như thế này:boost :: enable_if not in function signature

#include <iostream> 
#include <boost/type_traits/is_array.hpp> 
#include <boost/utility/enable_if.hpp> 
using namespace std; 

template <typename T, typename B=typename boost::enable_if< boost::is_array<T> >::type > void asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T, typename B=typename boost::disable_if< boost::is_array<T> >::type > void asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

int main() { 
    asd<int>(); 
    asd<int[]>(); 
} 

Ví dụ này làm g ++ phàn nàn:

../src/afg.cpp:10:97: error: redefinition of ‘template void asd()’

SFINAE có bản thân công trình, bởi vì nếu tôi xóa ví dụ số có disable_if, lỗi trình biên dịch là:

../src/afg.cpp:15:12: error: no matching function for call to ‘asd()’

Tôi muốn gì.

Vì vậy, có cách nào để hoàn thành SFINAE không có chữ ký "bình thường" của một hàm không, đó là danh sách đối số kiểu trả về?

EDIT: Đây là cuối cùng những gì tôi sẽ cố gắng trong mã thực:

#include <iostream> 
#include <type_traits> 
using namespace std; 

template <typename T, typename enable_if< is_array<T>::value, int >::type =0 > void asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T, typename enable_if< !is_array<T>::value, int >::type =0 > void asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

int main() { 
    asd<int[]>(); 
    asd<int>(); 
} 

tôi sử dụng C++ 0x thứ thay vì thúc đẩy bởi vì chừng nào tôi cần C++ 0x để sử dụng mặc định của đối số mẫu, tôi thấy không có lý do gì để sử dụng tăng, đó là tiền thân của nó.

Trả lời

6

Đối số mẫu mặc định không phải là một phần của chữ ký của mẫu chức năng. Nhưng loại tham số mẫu là.Vì vậy, bạn có thể làm như sau và có khả năng quá tải nó

template < 
    typename T, 
    typename boost::enable_if< 
    boost::is_array<T>, int 
    >::type = 0 
> 
void asd() { 
    cout<<"This is for arrays"<<endl; 
} 

template < 
    typename T, 
    typename boost::disable_if< 
    boost::is_array<T>, int 
    >::type = 0 
> 
void asd() { 
    cout<<"This is for arrays"<<endl; 
} 
+0

wow, tôi không biết rằng ngay cả loại mẫu có thể là động! Không chấp nhận câu trả lời của chúng tôi bởi vì tôi phải kiểm tra trước rằng thủ thuật này có thể áp dụng cho mã của tôi, nhưng tôi khá chắc chắn nó sẽ. –

8

Vì C++ 11 làm cho nó có thể, tôi chỉ sử dụng enable_if (hoặc ngược lại disable_if) bên trong đối số mẫu, cách bạn đang làm. Nếu/khi có một số tình trạng quá tải, thì tôi sử dụng các đối số mẫu mặc định, làm cho danh sách tham số mẫu khác nhau về tính chất. Vì vậy, để tái sử dụng ví dụ của bạn mà sẽ là:

template< 
    typename T 
    , typename B = typename boost::enable_if< 
     boost::is_array<T> 
    >::type 
> 
void asd() { 
    cout << "This is for arrays" << endl; 
} 

template< 
    typename T 
    , typename B = typename boost::disable_if< 
     boost::is_array<T> 
    >::type 
    , typename = void 
> 
void asd() { 
    cout << "This is for arrays" << endl; 
} 

Một lựa chọn khác để không rối tung các kiểu trả về (mà không có sẵn trong một số trường hợp, ví dụ như các nhà khai thác chuyển đổi) mà đã tồn tại từ C++ 03 là sử dụng đối số mặc định :

template<typename T> 
void 
foo(T t, typename std::enable_if<some_trait<T>::value>::type* = nullptr); 

Tôi không sử dụng biểu mẫu này vì tôi không thích 'lộn xộn' với các loại đối số giống như kiểu trả về và vì lý do nhất quán (vì không thể thực hiện được trong mọi trường hợp).

2

Điều này có thể không chính xác như những gì bạn đang yêu cầu, nhưng làm thế nào về chuyên môn mẫu cũ tốt?

template<typename T> 
struct asd 
{ 
    static void fgh() 
    { 
     std::cout << "not an array\n"; 
    } 
}; 

template<typename T> 
struct asd<T[]> 
{ 
    static void fgh() 
    { 
     std::cout << "an array of unknown size\n"; 
    } 
}; 

template<typename T, size_t N> 
struct asd<T[N]> 
{ 
    static void fgh() 
    { 
     std::cout << "an array of known size\n"; 
    } 
}; 

int main() 
{ 
    asd<int>::fgh(); 
    asd<int[]>::fgh(); 
    asd<int[42]>::fgh(); 
} 
+0

tốt, nhưng nó dài hơn câu trả lời khác. –

+3

Ít nhất nó in những thứ khác nhau cho mảng và không mảng ;-) – fredoverflow

2

So, is there a way to accomplish SFINAE not in the "normal" signature of a function, that is return type + argument list?

Vâng, có một cách để có được những kết quả tương tự mà không sử dụng SFINAE ở tất cả — quá tải:

#include <iostream> 
#include <type_traits> 

void asd_impl(std::true_type&&) 
{ 
    std::cout << "This is for arrays\n"; 
} 

void asd_impl(std::false_type&&) 
{ 
    std::cout << "This is not for arrays\n"; 
} 

template<typename T> 
void asd() 
{ 
    asd_impl(std::is_array<T>()); 
} 

int main() 
{ 
    asd<int>(); 
    asd<int[]>(); 
} 

phong cách này là xa dễ đọc hơn IMO, và được sử dụng rộng rãi trong template- các thư viện nặng như Boost. Spirit vì nó có xu hướng biên dịch nhanh hơn và hoạt động tốt hơn với các trình biên dịch có mẫu hỗ trợ SFINAE ít hơn sao/sao (ví dụ: VC++ và Sun Studio).

Online demo.

+0

+1, cho câu hỏi đúng/sai đơn giản, người ta không cần phải sử dụng SFINAE. Nó sẽ chỉ nhận được lông cho nhiều hơn một đặc điểm, vì bạn không thể chỉ đơn giản là kết hợp chúng với các toán tử logic. (Mà làm cho tôi tự hỏi nếu có thể quá tải 'nhà điều hành ||' và 'nhà điều hành &&' cho 'true_type' và' false_type' ... hm.) – Xeo

+0

@Xeo: 'std :: integral_constant 'nên có ngữ nghĩa thích hợp. – ildjarn

+0

Vâng, nhưng có vẻ hơi lộn xộn, đó là lý do tại sao tôi tự hỏi về quá tải nhà điều hành. :) Về cơ bản nó sẽ trông đẹp hơn. – Xeo

8

Vâng, tôi thường sử dụng các macro để làm enable_if xây dựng một sạch hơn rất nhiều (họ thậm chí làm việc trong hầu hết các trình biên dịch C++ 03):

#define ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE(...) __VA_ARGS__>::type 
#define FUNCTION_REQUIRES(...) typename boost::enable_if<boost::mpl::and_<__VA_ARGS__, boost::mpl::bool_<true> >, ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE 
#define EXCLUDE(...) typename boost::mpl::not_<typename boost::mpl::or_<__VA_ARGS__, boost::mpl::bool_<false> >::type >::type 

Sau đó, bạn sẽ xác định chức năng của bạn như thế này:

template <typename T > 
FUNCTION_REQUIRES(is_array<T>) 
(void) asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T > 
FUNCTION_REQUIRES(EXCLUDE(is_array<T>)) 
(void) asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

điều duy nhất là, bạn cần phải đặt dấu ngoặc xung quanh kiểu trả về. Nếu bạn quên chúng, trình biên dịch sẽ nói điều gì đó như 'ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE' không được xác định.