2012-06-26 27 views
6

Tôi đang viết một lớp học chia sẻ một số tính năng khác nhau với std::function (hoặc ít nhất các lớp học theo nhiều cách tương tự). Như bạn đã biết std::function được khởi tạo bằng cách chỉ định các tham số mẫu (ví dụ: std::function<void (std::string&)>), điều này cũng giống với lớp của tôi. Tôi có một ngoại lệ mặc dù, tôi muốn chuyên duy nhất chức năng trong lớp học của tôi, nếu giá trị trả lại là void (std::function<"return value" ("parameters">). Tôi cần điều này được thực hiện tại thời gian biên dịch, và tôi không thể làm cho nó hoạt động như mong muốn. Dưới đây là một số mã kiểm tra cho lời giải thích:Loại chuyên môn tại thời điểm biên dịch

#include <iostream> 
#include <type_traits> 

template <typename T> class Test { }; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    Ret operator()(Args...) 
    { 
     if(std::is_void<Ret>::value) 
     { 
      // Do something... 
     } 

     else /* Not a void function */ 
     { 
      Ret returnVal; 
      return returnVal; 
     } 
    } 
}; 

int main(int argc, char * argv[]) 
{ 
    Test<void (char)> test; 
    test('k'); 
} 

Như bạn có thể thấy rõ, nếu trình biên dịch không loại bỏ các chi nhánh 'khác' trong các thử nghiệm trên, mã của tôi sẽ cố gắng để tạo ra một giá trị void (tức là void returnVal;) . Vấn đề là trình biên dịch không loại bỏ các chi nhánh vì vậy tôi kết thúc với một lỗi biên dịch:

./test.cpp: In instantiation of ‘Ret Test::operator()(Args ...) [with Ret = void; Args = {char}]’: ./test.cpp:27:10: required from here ./test.cpp:18:8: error: variable or field ‘returnVal’ declared void ./test.cpp:19:11: error: return-statement with a value, in function returning 'void' [-fpermissive]

Người ta sẽ thường sử dụng kết hợp với std::enable_ifstd::is_void, vấn đề là tôi không muốn chuyên về chức năng mẫu, nhưng trên mẫu lớp.

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    typename std::enable_if<!std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...) 
    { 
     Ret returnVal; 
     return returnVal; 
    } 

    typename std::enable_if<std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...) 
    { 
     // It's a void function 
     // ... 
    } 
}; 

Nếu tôi sử dụng đoạn mã trên thay vào đó tôi kết thúc với lỗi thậm chí nhiều hơn và không có một giải pháp

./test.cpp:11:2: error: expected ‘;’ at end of member declaration 
./test.cpp:11:2: error: declaration of ‘typename std::enable_if<(! std::is_void<_Tp>::value), Ret>::type Test<Ret(Args ...)>::Ret’ 
./test.cpp:6:11: error: shadows template parm ‘class Ret’ 
./test.cpp:11:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive] 
./test.cpp:18:2: error: expected ‘;’ at end of member declaration 
./test.cpp:18:2: error: declaration of ‘typename std::enable_if<std::is_void<_Tp>::value, Ret>::type Test<Ret(Args ...)>::Ret’ 
./test.cpp:6:11: error: shadows template parm ‘class Ret’ 
./test.cpp:18:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive] 
./test.cpp:18:6: error: ‘int Test<Ret(Args ...)>::operator()(Args ...)’ cannot be overloaded 
./test.cpp:11:6: error: with ‘int Test<Ret(Args ...)>::operator()(Args ...)’ 
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...)’: 
./test.cpp:22:2: warning: no return statement in function returning non-void [-Wreturn-type] 
./test.cpp: In instantiation of ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’: 
./test.cpp:28:10: required from here 
./test.cpp:13:7: error: variable or field ‘returnVal’ declared void 
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’: 
./test.cpp:15:2: warning: control reaches end of non-void function [-Wreturn-type] 

Tôi xin lỗi nếu tôi chỉ đơn giản câm, và câu trả lời là rõ ràng. Tôi khá mới với các mẫu và tôi không thể tìm thấy một câu trả lời suiting trong bất kỳ chủ đề/câu hỏi khác.

+0

Có đề xuất để bao gồm "tĩnh nếu" sẽ thực hiện chính xác những gì bạn muốn trong ví dụ đầu tiên của mình. – mfontanini

+0

@mfontanini: Cho dù bất kỳ đề xuất nào trong số hai đề xuất có thực sự tiến về phía trước hay không là một vấn đề khác. Chúng có những thiếu sót và một số thứ trở nên phức tạp hơn nhiều so với khi bạn bắt đầu sử dụng SFINAE với các mẫu có static-if's –

+0

"Vấn đề là trình biên dịch không loại bỏ nhánh nên tôi sẽ gặp lỗi biên dịch" = > Thật không may, trình biên dịch * là bắt buộc không *, mà (tôi thừa nhận) là một chút đáng lo ngại. –

Trả lời

7

Có một vài điều không rõ ràng từ mô tả của bạn, vì vậy tôi sẽ bắt đầu với câu trả lời chung nhất.

Giả sử rằng mẫu có các chức năng khác mà hành vi phải được giữ nguyên và bạn chỉ muốn xác định lại hành vi cho hàm cụ thể đó, câu trả lời đơn giản nhất là chia mẫu thành hai và sử dụng thừa kế để hợp nhất chúng. Tại thời điểm này, bạn có thể sử dụng chuyên biệt từng phần mẫu trên mẫu cơ sở:

template <typename T, typename... Args> 
struct tmpl_base { 
    T operator()(Args... args) { 
     //generic 
    } 
}; 
template <typename... Args> 
struct tmpl_base<void,Args...> { 
    void operator()(Args... args) { 
    } 
}; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> : tmp_base<Ret,Args...> { 
    // do not declare/define operator(), maybe bring the definition into scope: 
    using tmp_base<Ret,Args...>::operator(); 

    // Rest of the class 

Nếu đây là chức năng duy nhất trong mẫu của bạn, thì một phần chuyên môn là giải pháp đơn giản hơn nhiều mà không yêu cầu thừa kế thừa kế.

+0

Tôi sẽ, theo nguyên tắc, tư vấn chống lại việc sử dụng thừa kế khi thành phần hoạt động (hoặc ở đây, ủy quyền cho một chức năng miễn phí), nhưng tôi phải thừa nhận rằng tôi có lẽ sẽ không nghĩ ra bất cứ điều gì đáng sợ. –

+0

@MatthieuM.: Đây có lẽ là sự lạm dụng duy nhất của thừa kế mà tôi tin là một điều ác cần thiết. Tôi không nghĩ rằng trong trường hợp này, bạn có thể thực hiện bất kỳ ủy quyền nào ngắn ở đây, vì vấn đề chính là biểu thức 'return delegate (args ...)' sẽ bị hỏng cho các trường hợp 'void' được trả về . Vấn đề cụ thể này ngăn chặn ủy quyền cho một đối tượng được soạn thảo hoặc một hàm khác. –

+0

Chúng tôi sẽ đoán điều này sẽ phải làm. Đó là buồn bao nhiêu mã là cần thiết cho một nhiệm vụ đơn giản như vậy. Tôi muốn C++ có [static if] (http://dlang.org/version.html#StaticIfCondition) 's: ( –

2

Một giải pháp là một phần chuyên về mẫu lớp học của bạn.

#include <iostream> 
#include <type_traits> 

template <typename T> class Test { }; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    Ret operator()(Args...) 
    { 

     std::cout<<"non-void function"<<std::endl; 
     Ret returnVal; 
     return returnVal; 

    } 
}; 

template <typename... Args> 
class Test<void (Args...)> 
{ 
public: 
    void operator()(Args...) 
    { 

     std::cout<<"void function"<<std::endl; 
    } 
}; 


int main(int argc, char * argv[]) 
{ 
    Test<void (char)> test; 
    test('k'); 
} 
Các vấn đề liên quan