C++ template specialization on functions - c++

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();
}

Related

How to explicitly tell compiler to choose exactly one parameter template during template pack expansion?

I'm trying using pack parameter template to fit some cases. I want to stop the template packed parameter expanding when there is only one parameter in the list. I want to use the typename explicitly when instancing the template, rather than using the typename to declare variables.
Here is a minimal example:
template <class T>
void f() {}
template <class T, class... Args>
void f() {
f<Args...>();
}
int main() {
f<int, int, double>();
return 0;
}
When compiling the code, I got the error:
demo.cc: In instantiation of ‘void f() [with T = int; Args = {double}]’:
demo.cc:6:13: required from ‘void f() [with T = int; Args = {int, double}]’
demo.cc:10:23: required from here
demo.cc:6:13: error: call of overloaded ‘f<double>()’ is ambiguous
6 | f<Args...>();
| ~~~~~~~~~~^~
demo.cc:2:6: note: candidate: ‘void f() [with T = double]’
2 | void f() {}
| ^
demo.cc:5:6: note: candidate: ‘void f() [with T = double; Args = {}]’
5 | void f() {
| ^
I have read the following information from cppreferrence here:
A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order.
It may be why the compiler can't decide whether to use void f() [with T = double] or void f() [with T = double; Args = {}]
Can I stop the unpacking only by using template parameters rather than using input argument of function?
You can constrain the variadic version of the function with SFINAE to stop it from being called if the parameter pack is empty. That would look like
template <class T, class... Args, std::enable_if_t<(sizeof...(Args) > 0), bool> = true>
void f() {
f<Args...>();
}

Incorrect template substitution error with g++8.3

I have the following program -
class foo {
typedef foo type;
};
template<typename A>
using type_t = typename A::type;
template <typename T>
type_t<T> bar(void){
return type_t<T>();
}
int main() {
bar<foo>();
return 0;
}
It is malformed because you cannot access foo::type inside bar because it is private (if I make type public, the program compiles fine.)
But when I try to compile this with g++8.3, I get the following error -
prog.cpp: In function ‘int main()’:
prog.cpp:16:11: error: no matching function for call to ‘bar<foo>()’
bar<foo>();
^
prog.cpp:11:11: note: candidate: ‘template<class T> type_t<T> bar()’
type_t<T> bar(void){
^~~
prog.cpp:11:11: note: template argument deduction/substitution failed:
prog.cpp: In substitution of ‘template<class A> using type_t = typename A::type [with A = foo]’:
prog.cpp:8:32: recursively required by substitution of ‘template<class A> using type_t = typename A::type [with A = foo]’
prog.cpp:8:32: required by substitution of ‘template<class A> using type_t = typename A::type [with A = foo]’
prog.cpp:11:11: required by substitution of ‘template<class T> type_t<T> bar() [with T = foo]’
prog.cpp:16:11: required from here
prog.cpp:8:32: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
using type_t = typename A::type;
Live here on Ideone
What is happening here? Is this a compiler bug or am I not understanding something correctly? Is the typedef being marked as private cause SFINAE to kick in somehow causing it to look for alternate substitutions that are recursive?
Btw, I get the correct error about type being private if I don't use the type alias type_t and directly define the function bar as -
template <typename T>
typename T::type bar(void) {
return typename T::type();
}
The error also has nothing to do with the fact that foo::type is foo. If I change the typedef to typedef int type, I still get the same error about recursive substitution.

C++ Concepts TS: Require nested templated type alias

For a generic library I'm trying to define a concept in terms of having a correct implementation of a traits struct. In particular I want to check that the user has provided all required nested types, static member functions and data members. However, I can't find a way to require a nested templated type(-alias).
I have a declaration of the traits struct
template <typename>
struct trait;
and a specialization for chars
template <>
struct trait<char> {
template <typename>
using type = int;
};
I now define my concept in terms of this trait
template <typename T>
concept bool SatisfiesTrait = requires() {
typename trait<T>; // require the user to have spcialized
// for their type
typename trait<T>::type<long long>; // require the nested template
};
as well as a function requiring a type satisfying this concept
constexpr bool foo(SatisfiesTrait) { return true; }
In my main method I then try to call this function with a char:
int main() {
foo('c');
}
When compiling all this with GCC I get the error message
prog.cc:15:24: error: 'trait<T>::type' is not a type
typename trait<T>::type<long long>;
^~~~
prog.cc: In function 'int main()':
prog.cc:26:11: error: cannot call function 'constexpr bool foo(auto:1) [with auto:1 = char]'
foo('c');
^
prog.cc:18:16: note: constraints not satisfied
constexpr bool foo(SatisfiesTrait) {
^~~
prog.cc:18:16: note: in the expansion of concept 'SatisfiesTrait<auto:1>' template<class T> concept const bool SatisfiesTrait<T> [with T = char]
However, when I change my main function to
int main() {
typename trait<char>::type<long long> v;
(void) v;
foo('c');
}
and comment out the requirement of the nested alias template it compiles just fine. The same problem occurs when the nested type has a non-type template parameter instead of a type parameter.
Am I doing something wrong here or is this a bug in GCCs implementation of the Concepts TS?
The code can also be found on Wandbox.

Overload resolution between template members in base and derived classes

Microsoft compiler (Visual Studio 2017 15.2) rejects the following code:
#include <type_traits>
struct B
{
template<int n, std::enable_if_t<n == 0, int> = 0>
void f() { }
};
struct D : B
{
using B::f;
template<int n, std::enable_if_t<n == 1, int> = 0>
void f() { }
};
int main()
{
D d;
d.f<0>();
d.f<1>();
}
The error is:
error C2672: 'D::f': no matching overloaded function found
error C2783: 'void D::f(void)': could not deduce template argument for '__formal'
note: see declaration of 'D::f'
Clang also rejects it:
error: no matching member function for call to 'f'
d.f<0>();
~~^~~~
note: candidate template ignored: disabled by 'enable_if' [with n = 0]
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
GCC perfectly accepts it. Which compiler is right?
Addition:
With SFINAE in the form
template<int n, typename = std::enable_if_t<n == 0>>
...
template<int n, typename = std::enable_if_t<n == 1>>
GCC also produces an error:
error: no matching function for call to ‘D::f<0>()’
d.f<0>();
^
note: candidate: template<int n, class> void D::f()
void f()
^
note: template argument deduction/substitution failed:
Turning cppleaner's comment into an answer:
From namespace.udecl#15.sentence-1:
When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting)
Unfortunately, template parameter doesn't count and both f has empty parameter-type-list, are not const and no ref-qualifier.
Derived::f so hides Base::f.
gcc is wrong to accept that code.
So the way to fix it is by default argument (returned type doesn't count either):
struct B
{
template <int n>
void f(std::enable_if_t<n == 0>* = nullptr) { }
};
struct D : B
{
using B::f;
template <int n>
void f(std::enable_if_t<n == 1>* = nullptr) { }
};

Type specialization at compile-time

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