Type of specialization at compile time

I am writing a class that has several different functions with std::function (or at least the classes are much alike). As you know, std::function is created by specifying template parameters (for example, std::function<void (std::string&)> ), for my class it is the same. I have an exception, although I want to specialize a single function in my class if the return value is invalid ( std::function<"return value" ("parameters"> ). I need this to be done at compile time, and I just can't make it work properly. Here is a sample code to explain:

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

As you can clearly see, if the compiler does not remove the else branch in the above test, my code will try to create a void value (ie void returnVal; ). The problem is that the compiler does not delete the branch, so I end up with a compiler error:

./test.cpp: when creating "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 value, in function returning 'void' [-fpermissive]

You can usually use std::enable_if in combination with std::is_void , the problem is that I do not want to specialize in the function template, but in the class template.

 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 a void function // ... } }; 

If I use the above code, instead I get even more errors and without a solution

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

Sorry if I'm just dumb and the answer is obvious. I am new to templates and I could not find a suitable answer in any of the other questions / questions.

+6
source share
2 answers

There are a few things that are not entirely clear from your description, so I will start with the most general answer.

Assuming the template has other functions for which the behavior should be the same, and you only want to override the behavior for this particular function, the simplest answer is to split the template into two parts and use inheritance to merge them. At this point you can use partial specialization templates on the base template:

 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 

If this is the only function in your template, then partial specialization is a much simpler solution that does not require abuse of inheritance.

+7
source

One solution partially specializes in your class template.

 #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'); } 
+2
source

Source: https://habr.com/ru/post/919046/


All Articles