Type specialization at compile-time - c++

I'm writing a class which shares several different features with std::function (or at least the classes are in many ways similar). As you all know std::function is instantiated by specifying the template parameters (i.e std::function<void (std::string&)>), it is the same for my class. I have an exception though, I want to specialize a single function in my class, if the return value is void (std::function<"return value" ("parameters">). I need this to be done at compile time, and I just can't make it work as it should. Here is some test code for explanation:
#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 (i.e void returnVal;). The problem is that the compiler does not remove the branch so I end up with a compiler error:
./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]
One would normally use std::enable_if combined with std::is_void, the problem is that I don't want to specialize on the function template, but on 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's a void function
// ...
}
};
If I use the above code instead I end up with 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]
I'm sorry if I'm just plain dumb, and the answer is obvious. I'm fairly new to templates and I couldn't find a suiting answer in any of the other threads/questions.

There are a few things that are not exactly clear from your description, so I will start with the most general answer.
Assuming that the template has other functions for which the behavior must be kept the same, and you only want to redefine the behavior for that particular function, the simplest answer is to split the template in two, and use inheritance to merge them. At this point you can use partial template specialization 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 abusing inheritance.

One solution is partially specializing 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');
}

Related

How to deduce type of `T` from a pointer to member function?

I have a template, more or less like this:
template<typename T,void (T::*F)()>
struct Foo{
/* ... do some stuff with the member function pointer ...*/
//... e.g.
T foo(){
T t;
t.*F;
return t;
};
it works, but I dont like the way I have to instantiate it:
Foo<SomeVeryLongClassName,&SomeVeryLongClassName::AnEvenLongerMemberFunctionName> f;
Is there some way I can make the template deduce T?
I was thinking of a template method that I could call like this:
getFoo(&SomeVeryLongClassName::AnEvenLongerMemberFunctionName);
or, as I will mainly use Foo inside T, that would be just
getFoo(AnEvenLongerMemberFunctionName);
I tried this
#include <iostream>
template <typename T,void (T::*MEMFUN)()>
struct Foo{};
template <typename T,void (T::*MEMFUN)()>
Foo<typename T,typename MEMFUN> getFoo(MEMFUN f){
return Foo<typename T,typename MEMFUN>();
}
struct Bar { void test(){ std::cout << "MUH" << std::endl;} };
int main (){ getFoo(&Bar::test); }
The error messages are actually quite clear, but I dont understand them at all...
templateExample.cpp:9:28: error: wrong number of template arguments (1, should be 2)
Foo<typename T,typename MEMFUN>
^
templateExample.cpp:4:8: error: provided for ‘template<class T, void (T::* MEMFUN)()> struct Foo’
struct Foo{
^
templateExample.cpp:10:7: error: invalid type in declaration before ‘(’ token
getFoo(MEMFUN f){
^
templateExample.cpp:10:7: error: template declaration of ‘int getFoo’
templateExample.cpp:10:15: error: expected ‘)’ before ‘f’
getFoo(MEMFUN f){
^
templateExample.cpp: In function ‘int main()’:
templateExample.cpp:20:20: error: ‘getFoo’ was not declared in this scope
getFoo(&Bar::test);
...why "wrong number of template arguments (1, should be 2)" ?
How can I help the compiler to deduce T when instantiating a Foo ?
Is it possible with only pre-C++11?
PS: this is very close to being a dupe, but I really need to know the type of T and not just call the member function (e.g. I need to create an instance).
In C++17 we have non-type template parameters with deduced types:
template <auto> struct Foo;
template <typename T, void (T::*MF)()> struct Foo<MF> {
// ...
};
Usage: Foo<&X::f>
You can also directly use template <auto X> and either keep using auto inside your template or use decltype(X) to get at the type of the non-type parameter.
Prior to C++17, you could try to perform deduction via some contortions involving helper class templates with member function templates and decltype.
The gory details:
If you define a function template template <typename T, void(T::*MF)()> Foo<T, MF> f(MF);, where Foo is your old-style class template (like template <typename T, void (T::*MF)()> class Foo;), then you can use decltype(f(&X::h)) to deduce the desired type Foo<X, &X::h> without having to repeat X. The price is that you either need to say decltype everywhere, or you wrap that in a macro.

dependent-name T is parsed as a non-type, but instantiation yields a type

I realize that there have been some questions on SO touching this subject but I haven't found the solution yet.
I would like to enable function f only when all of its arguments are pod.
I have the following piece of code to do this:
#include <type_traits>
template <typename... Conds>
struct and_ : std::true_type
{
};
template <typename Cond, typename... Conds>
struct and_<Cond, Conds...> :
std::conditional<Cond::value, and_<Conds...>, std::false_type>::type
{
};
template <typename... T>
using are_all_pod = and_<std::is_pod<T>...>;
template<typename... Args,
typename = typename std::enable_if<are_all_pod<Args...>::type>>
void f(Args ... args)
{
}
int main()
{
f(1,2);
}
I do receive the following error:
main.cpp: In function ‘int main()’:
main.cpp:25:8: error: no matching function for call to ‘f(int, int)’
f(1,2);
^
main.cpp:19:6: note: candidate: template<class ... Args, class> void f(Args ...)
void f(Args ... args)
^
main.cpp:19:6: note: template argument deduction/substitution failed:
main.cpp:18:10: error: dependent-name ‘and_<std::is_pod<T>...>::type’ is parsed as a non-type, but instantiation yields a type
typename = typename std::enable_if<are_all_pod<Args...>::type>>
^
main.cpp:18:10: note: say ‘typename and_<std::is_pod<T>...>::type’ if a type is meant
I tried following compiler's advice but I get the same error then. Using gcc 5.2.1
std::enable_if<are_all_pod<Args...>::type> makes no sense.
Change it to something like std::enable_if<are_all_pod<Args...>::value, void>::type and the program will compile.

Partial specialization of variadic template member function

I'm struggling with specializations of member functions when they are templated using variadic template.
The following example specializes a whole class and it works fine:
template<typename... Args>
class C;
template<class T, typename... Args>
class C<T, Args...> { };
template<>
class C<> { };
int main() {
C<int, double> c{};
}
The following one does not, even though the idea behind it is exactly the same of the one above:
class F {
template<typename... Args>
void f();
};
template<class T, typename... Args>
void F::f<T, Args...>() { }
int main() {
}
I'm getting the following error and I don't understand what it's due to:
main.cpp:7:23: error: non-type partial specialization ‘f<T, Args ...>’ is not allowed
void F::f<T, Args...>() { }
^
main.cpp:7:6: error: prototype for ‘void F::f()’ does not match any in class ‘F’
void F::f<T, Args...>() { }
^
main.cpp:3:10: error: candidate is: template<class ... Args> void F::f()
void f();
^
Is there some constraints I'm not aware of when specializing function template?
G++ version is: g++ (Debian 5.2.1-23) 5.2.1 20151028
EDIT
By the way, the actual problem I'm getting from the real code is:
non-class, non-variable partial specialization ‘executeCommand<T, Args ...>’ is not allowed
Anyway, the reduced example is similar to the real one. I hope the errors are not completely unrelated.
You cannot partially specialize function templates; only explicit specialization is allowed.
You can get pretty much the same effect using overloading, especially if you use concepts such as tag dispatching.

How to force template substitution failure unless T::answer is in integral type?

I have the following code:
#include <type_traits>
struct SA {};
struct SB { static const int answer = 42; };
const int SB::answer;
template <typename T>
int F() {
return T::answer;
}
int main(int argc, char **argv) {
(void)argc; (void)argv;
// F<SA>(); // I want to make this a template substitution failure.
return F<SB>(); // This should still work.
}
I want to make the call F<SA>() become a template substitution failure. I've tried changing int F to typename std::enable_if<std::is_integral<T::answer>::value, int>::type F, but I got the following error:
$ g++ --versiong++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
$ g++ -W -Wall -Wextra -Werror -std=c++0x -fno-diagnostics-show-caret ei.cc && ./a.out
ei.cc:8:55: error: to refer to a type member of a template parameter, use ‘typename T:: answer’ [-fpermissive]
ei.cc: In function ‘int main(int, char**)’:
ei.cc:16:20: error: no matching function for call to ‘F()’
ei.cc:16:20: note: candidate is:
ei.cc:8:76: note: template<class T> typename std::enable_if<std::is_integral<typename T::answer>::value, int>::type F()
ei.cc:8:76: note: template argument deduction/substitution failed:
ei.cc: In substitution of ‘template<class T> typename std::enable_if<std::is_integral<typename T::answer>::value, int>::type F() [with T = SB]’:
ei.cc:16:20: required from here
ei.cc:8:76: error: no type named ‘answer’ in ‘struct SB’
This is trying to look for a type named answer in struct SB, but I want to make it look for an integral field named answer in struct SB.
I find putting the enable_if check in the template arguments makes your interface a bit clearer:
template <typename T,
std::enable_if_t<std::is_integral<decltype(T::answer)>::value>* = nullptr>
int F() {
return T::answer;
}
Or even clearer, using R. Martinho Fernandes' Remastered enable_if.
namespace detail {
enum class enabler {};
}
template <typename Condition>
using EnableIf = std::enable_if_t<Condition::value, detail::enabler>;
template <typename T, EnableIf<std::is_integral<decltype(T::answer)>>...>
int F() {
return T::answer;
}
Adding decltype(...) seems to work: changing int F to typename std::enable_if<std::is_integral<decltype(T::answer)>::value, int>::type F.

C++ template specialization on functions

I'm playing around with template specialization, and I've found an issue I can't seem to solve; this is my code:
template<int length, typename T>
void test(T* array)
{
...
test<length-1>(array);
}
template<typename T>
void test<0>(T* array)
{
return;
}
So what I'm trying to do, is to pass the length, of what's to be processed in the template.
The problem is, that the compilation of this, well outputs forever:
a.cpp:83:43: error: template-id 'test<0>' in declaration of primary template
a.cpp: In function 'void test(T*) [with int length= -0x000000081, T = int]':
a.cpp:77:9: instantiated from 'void test(T*) [with int length= -0x000000080, T = int]'
a.cpp:77:9: instantiated from 'void test(T*) [with int length= -0x00000007f, T = int]'
a.cpp:77:9: [ skipping 151 instantiation contexts ]
a.cpp:77:9: instantiated from 'void test(T*) [with int length= 28, T = int]'
a.cpp:77:9: instantiated from 'void test(T*) [with int length= 29, T = int]'
...
a.cpp: In function 'void test(T*) [with int length= -0x000000082, T = int]':
a.cpp:77:9: instantiated from 'void test(T*) [with int length= -0x000000081, T = int]'
a.cpp:77:9: instantiated from 'void test(T*) [with int length= -0x000000080, T = int]'
Last two lines, is pretty much the same as the first ones.
To me it would seem, its not catching the specialization, hence:
a.cpp:83:43: error: template-id 'test<0>' in declaration of primary template
Am I correct?
And if I'm correct, I'm guessing it's the issue that partial template specialisation is not allowed for function templates, so what would be the solution then, making a struct, and using specialisation on that?
Partial specialization of function templates is not allowed. Herb Sutter explains why in his article "Why Not Specialize Function Templates?".
To work around this limitation you need to use class templates instead. You can then write a regular function template that uses that class template.
That specific error you're getting is because you forgot the second parameter in your specialization. If you this instead:
template<int length, typename T>
void test(T* array)
{
//...
test<length-1,T>(array);
}
template<typename T>
void test<0,T>(T* array)
{
return;
}
GCC complains with the following:
error: function template partial specialization 'test<0, T>' is not allowed
Functions cannot be partially specialized. To work around this, have your template function call a function in a class:
template<int length, typename T>
struct helper
{
static void go(T* array)
{
...
helper<length-1, T>::go(array);
}
};
template<typename T>
struct helper<0, T>
{
static void go(T* array)
{
...
}
};
template<int length, typename T>
void test(T* array)
{
...
helper<length, T>::go(array);
}
Partial specialization of function templates is not allowed.
To work around that, you could make test a static member of a class template, and partially specialize the class.
You can workaround with a helper class. For illustrational purposes:
template<typename T, typename U> struct helper_t {
static int foo () { return 0; }
};
template<typename T> struct helper_t<T,T> {
static int foo () { return 1; }
};
template <typename T, typename U>
int frob () {
return helper_t<T,U>::foo();
}