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_if
std::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.
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
@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 –
"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. –