C++ inherit template specializations - c++

I have a template function that does something depending on the type passed as a template argument:
template<class T>
void foo() {
if constexpr (std::is_integral_v<T>) {
// ...
} else if constexpr ... {
// ...
}
}
The problem with this it that I don't really need to have a function instantiation for every possible integral type (to avoid template code bloat), so I would like to have only one instance that takes a int and if we pass the type char or short it calls the int version. I would like something like this:
template<class T>
void foo() {
if constexpr (std::is_same_v<T, int>) { // Only check for `int`, because "lower" types will be converted to int
// ...
} else if constexpr ... {
// ...
}
}
foo<short>(); // Call `foo<int>`
foo<char>(); // Call `foo<int>`
foo<int>(); // Call `foo<int>`
Maybe I can wrap the function inside a struct and have a struct<char> that extends the struct<int>?
Note that there is no "values" anywhere, I just have template parameters.

You can turn foo into a callable object, and use using alias to select different instantiations according to T, for example:
#include <type_traits>
template<class T>
struct foo {
void operator()() {
if constexpr (std::is_same_v<T, int>) {
// ...
} else if constexpr ... {
// ...
}
}
};
template<class T>
auto bar = std::conditional_t<std::is_integral_v<T>, foo<int>, foo<T>>();
int main() {
bar<short>(); // Call `foo<int>`
bar<char>(); // Call `foo<int>`
bar<int>(); // Call `foo<int>`
bar<const char*>(); // Call `foo<const char*>`
}

Related

Is it possible to deduct the template type of a templated parameter in C++?

I have a template class, with an internal method that is itself templated.
Consider the following minimal working example
#include <functional>
template<typename T>
class P{
public:
template <typename U>
P<U> call(std::function< P<U> (T)> f){
return f(return_value);
}
T return_value;
};
P<int> foo() {
P<int> value = P<int>();
return value;
}
P<float> bar(int arg) {
return P<float>();
}
int main()
{
auto res = foo().call<float>(bar);
return 0;
}
As you can see in the main function, the compiler forces me to to specify the float type for calling the call() function, eventhough the type should be obvious from passing over the bar() function, that specifies the float type.
Is it somehow possible to deduct the type so that the code in the main function can be simplified to the following statement:
auto res = foo().call(bar);
std::function is not the type that you should use whenever you want to pass a function around. std::function is a type that uses type erasure to be able to store different types of callables in one type. If you don't need that then you need no std::function:
#include <functional>
template<typename T>
class P{
public:
template <typename F>
auto call(F f){
return f(return_value);
}
T return_value{}; // don't forget to initialize !
};
P<int> foo() {
P<int> value = P<int>();
return value;
}
P<float> bar(int arg) {
return P<float>();
}
int main()
{
auto res = foo().call(bar);
return 0;
}
Using partial specializatzion you can get the return type of bar and you can get the float from P<float>:
#include <type_traits>
#include <iostream>
template <typename T> class P;
// get return type from function
template <typename T> struct return_type;
template <typename R,typename...args>
struct return_type<R(args...)> { using type = R; };
// get T from P<T>
template <typename P> struct P_arg;
template <typename T> struct P_arg< P<T> > { using type = T; };
// combine both
template <typename F>
struct get {
using type = typename P_arg<typename return_type<F>::type >::type;
};
template<typename T>
class P{
public:
template <typename F>
auto call(F f){
return f(return_value);
}
T return_value{};
};
P<float> bar(int arg) {
return P<float>();
}
int main()
{
std::cout << std::is_same_v< get<decltype(bar)>::type,float>;
return 0;
}
Though that does not really help here, because you cannot use it to decalre the return type of P::call, as it requires P<float> to be already complete.

Type-checking in template function returning given type

In a function like the following:
template <typename T>
T foo () {
std::string str("foo");
if (typeid(T) == typeid(int)) return (T)(1); // Cast to T is required in order to make sure the function returns T
else if (typeid(T) == typeid(std::string)) return (T)str;
// ..
}
It will get a compile error because the compiler will go ahead and test all possible values of T on every single line, and there is no conversion from std::string to int, even though I already did a type-check.
How should I approach this problem?
In C++17 and later, you can use if constexpr with std::is_same_v:
template <typename T>
T foo ()
{
if constexpr (std::is_same_v<T, int>)
return 1;
else if constexpr (std::is_same_v<T, std::string>)
return "foo";
// ..
}
This allows the compiler to optimize the ifs at compile-time and eliminate any unused branches from evaluation. For instance, when T is int then foo<int>() will simply be:
int foo ()
{
return 1;
}
And when T is std::string then foo<std::string>() will simply be:
std::string foo ()
{
return "foo";
}
Prior to C++17, you have to use template specialization instead to accomplish something similar, eg:
template <typename T>
T foo ()
{
return T();
}
template<>
int foo<int> ()
{
return 1;
}
template<>
std::string foo<std::string> ()
{
return "foo";
}
Alternatively, use the specialization on a helper function/struct, especially if foo() has other code that is invoked regardless of the type of T, eg:
template<typename T>
T someValue()
{
return T();
}
template<>
int someValue<int>()
{
return 1;
}
template<>
std::string someValue<std::string>()
{
return "foo";
}
template <typename T>
T foo ()
{
...
return someValue<T>();
}
template<typename T>
struct someType
{
static const T value = T();
}
template<>
struct someType<int>
{
static const int value = 1;
}
template<>
struct someType<std::string>
{
static const std::string value = "foo";
}
template <typename T>
T foo ()
{
...
return someType<T>::value;
}
Use if constexpr:
#include <type_traits>
template <typename T>
T foo () {
std::string str("foo");
if constexpr (std::is_same_v<T, int>) return (T)(1); // Cast to T is required in order to make sure the function returns T
else if constexpr (std::is_same_v<T, std::string>) return (T)str;
// ..
}
This compiles the inner block only if the constant expression in its argument evaluates to a value considered true. The syntax needs to be correct in the inner block, though. The is_same_v checks if the types are the same.

How to call a function, passing the return value (possibly void) of a functor?

Given a function that calls a templated function argument and calls another function that does something with the returned value:
template <typename T>
void doSomething(T &&) {
// ...
}
template <typename T_Func>
void call(T_Func &&func) {
doSomething(func());
}
How can this be extended to work with a functor that returns void? Ideally, I would like to add an overloaded void doSomething() { ... } that is called if func's return type is void.
Currently this just results in an error: invalid use of void expression if func returns void.
Working example on Ideone
I think you could create a struct helper to use overloaded , operator more or less like this:
#include <type_traits>
#include <utility>
struct my_void { };
struct my_type { };
template <class T, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
my_void operator,(T, my_type) { return {}; }
template <class T, typename std::enable_if<!std::is_void<T>::value>::type* = nullptr>
T &&operator,(T &&val, my_type) { return std::forward<T>(val); }
template <typename T>
void doSomething(T &&) {
}
template <typename T_Func>
void call(T_Func &&func) {
doSomething((func(), my_type{}));
}
int main() {
auto func1 = []() -> bool { return true; };
auto func2 = []() -> void { };
call(func1);
call(func2);
}
[live demo]
Edit:
Thanks to Piotr Skotnicki and Holt (they pointed out that the first overload actually wouldn't ever be triggered and proposed simplified version of the approach):
#include <type_traits>
#include <utility>
struct dumb_t { };
template <class T>
T &&operator,(T &&val, dumb_t) { return std::forward<T>(val); }
template <typename T>
void doSomething(T &&) {
}
template <typename T_Func>
void call(T_Func &&func) {
doSomething((func(), dumb_t{}));
}
int main() {
auto func1 = []() -> bool { return true; };
auto func2 = []() -> void { };
call(func1);
call(func2);
}
[live demo]
doSomething() takes a parameter, and a parameter cannot be void.
So, in order for this to work, you also need an overloaded doSomething() that takes no parameters. This is going to be the first step:
template <typename T>
void doSomething(T &&) {
// ...
}
void doSomething()
{
}
So, you're going to have to do this first, before you can even get off the ground.
It's also possible that you would like to supply a default value for the parameter, in case the functor returns a void; and still use a single template. That's another possibility, and the following solution can be easily adjusted, in an obvious way, to handle that.
What needs to happen here is a specialization of call() for a functor that returns a void. Unfortunately, functions cannot be partially specialized, so a helper class is needed:
#include <utility>
template <typename T>
void doSomething(T &&) {
// ...
}
void doSomething()
{
}
// Helper class, default implementation, functor returns a non-void value.
template<typename return_type>
class call_do_something {
public:
template<typename functor>
static void call(functor &&f)
{
doSomething(f());
}
};
// Specialization for a functor that returns a void.
//
// Trivially changed to call the template function instead, with
// a default parameter.
template<>
class call_do_something<void> {
public:
template<typename functor>
static void call(functor &&f)
{
f();
doSomething();
}
};
// The original call() function is just a wrapper, that selects
// the default or the specialized helper class.
template <typename T_Func>
void call(T_Func &&func) {
call_do_something<decltype(func())>::call(std::forward<T_Func>(func));
}
// Example:
void foobar()
{
call([] { return 1; });
call([] {});
}
You can provide an extra pair of overloaded helper functions (named callDispatch in my example below) to dispatch the call as required, which eliminates the need for partial specialisation and thus helper classes. They are overloaded by using different signature specifications for the std::function objects they take (live code here):
#include <iostream>
#include <functional>
int func1()
{
return 1;
}
void func2()
{
}
template <typename T>
void doSomething(T &&)
{
std::cout << "In param version" << std::endl;
// ...
}
void doSomething()
{
std::cout << "In no-param version" << std::endl;
// ...
}
template <typename R, typename ... Args>
void callDispatch(std::function<R(Args...)> &&f)
{
doSomething(f());
}
template <typename ... Args>
void callDispatch(std::function<void(Args...)> &&f)
{
f();
doSomething();
}
template <typename T>
void call(T &&func) {
callDispatch(std::function<std::remove_reference_t<T>>(func));
}
int main() {
call(func1);
call(func2);
}
One lean variant would be to give the method a function as parameter. Then you evaluate the expression inside the method and see if did anything. In general this is usally bad practice, since you usually can infer how it returns stuff and when it is needed.

C++ template, static function specialization

I have a syntax error with my template
I would like to partial specialize a static function of my template class
class.hpp
template <typename Foo, size_t bar = 26>
class MyClass
{
MyClass();
static void function();
};
#include "class.tpp"
class.tpp
template <typename Foo, bar>
MyClass<Foo, bar>::MyClass()
{ }
template <typename Foo>
inline
void
MyClass<Foo, 6>::function()
{
// ...
}
template <typename Foo>
inline
void
MyClass<Foo, 26>::function()
{
// ...
}
error: template definition of non-template
I just want to implement MyClass<Foo, bar>::function for bar == 26 and bar == 6
How to do that properly ?
Thanks
The function is not a template itself, it is only inside a class template. You can specialize the class for those cases, but not the function itself.
template <class Foo>
class MyClass<Foo, 26>
{
static void function() { ... }
};
Provided you have specialized the class like so, you can only declare the function inside the class, and define it outside like so:
template <class Foo>
void MyClass<Foo, 26>::function() { ... }
If you don't specialize it beforehand, you'll get a compilation error for using an incomplete type.
You might also find this question and answer on specializing a single function inside a class template relevant.
You cannot partial specialize method like that.
You may partial specialize the whole class.
or as alternative, you may forward the implementation to some helper:
struct that you may specialize as you want.
overload (using some dispatching):
namespace detail
{
template <typename Foo, std::size_t bar>
void function_impl(MyClass<Foo, bar>& that)
{
// Generic case.
}
template <typename Foo>
void function_impl(MyClass<Foo, 6>& that)
{
// special case <Foo, 6>.
}
template <typename Foo>
void function_impl(MyClass<Foo, 26>& that)
{
// special case <Foo, 26>.
}
}
template <typename Foo, std::size_t bar>
inline
void
MyClass<Foo, bar>::function()
{
detail::function_impl(*this);
}
After doing some research; Partial Specialization for member functions of a class template are not allowed, so one would have to specialize the whole class which can be a problem if the actual class is quite large. If you are trying to separate the implementation from the declaration having a wrapper or helper will work, but you must defined that and the partial specialization first. Check out this code here for it compiles, builds and outputs the appropriate values using MSVC 2015 CE.
MyClass.h
#ifndef MY_CLASS_H
#define MY_CLASS_H
#include <iostream>
// Helper - Wrapper Class
template<typename Foo, size_t Bar>
class specialized {
public:
inline static void function();
};
// Partial Specialization
template<typename Foo>
class specialization<Foo, 6> {
public:
inline static void function();
};
// Actual Class
template<typename Foo, size_t Bar = 26>
class MyClass {
private:
specialized<Foo, Bar> func;
public:
MyClass();
inline void function(); // Works
// inline static void function(); // Compiler Error
}; // MyClass
#include "MyClass.inl"
#endif // MY_CLASS_H
MyClass.inl
// Helper - Wrapper
template<typename Foo, size_t Bar>
inline void specialized<Foo, Bar>::function() {
std::cout << "26" << std::endl;
} // function
// Specialized
template<typename Foo>
inline void specialized<Foo, 6>::function() {
std::cout << "6" << std::endl;
} // function
// Constructor
template<typename Foo, size_t Bar>
MyClass<Foo, Bar>::MyClass() {
} // MyClass
// Class Member Function
template<typename Foo, size_t Bar>
inline void MyClass<Foo, Bar>::function() {
func.function();
} // function
MyClass.cpp
#include "MyClass.h"
Main.cpp
#include "MyClass.h"
int main() {
MyClass<float, 6> a;
a.function(); // Prints Out 6
MyClass<float, 26> b;
b.function(); // Prints Out 26
MyClass<float> c;
c.function(); // Prints Out 26
MyClass<float, x != 6> d;
d.function(); // Prints Out 26
return 0;
} // Main

Class template: why can't I specialize a single method for void type?

I have a class template:
template <typename Argument> class Signal
{
void invoke(Argument arg) {}
};
Now I want to invoke signals without parameter (meaning void parameter). I assume I could specialize the whole class for void and it would compile. But there's a lot of code in the class, I don't want to duplicate it. I want to only specialize whatever neccessary. So I try adding:
// void specialization
template<>
void Signal<void>::invoke()
{
}
And get an error:
error C2244: 'Signal::invoke' : unable to match function
definition to an existing declaration
Why is that?
The same code works for any type other than void.
The specialization for this template:
template <typename Argument> class Signal
{
void invoke(Argument arg) {}
};
would be:
template<>
void Signal<void>::invoke(void arg)
{
}
which is illegal because you can't have a void object.
One way of doing what you want is to use overloading to declare both invoke methods, and use some templating tricks (I believe this one is called SFINAE) to only allow the correct overload available based on your class template argument:
template <typename Argument> class Signal
{
public:
static constexpr bool IsVoid = is_same<Argument, void>::value;
template <typename T = Argument, typename = typename std::enable_if< !IsVoid && is_same<Argument, T>::value>::type >
void invoke(T arg) {
// only available for non-void types
}
template <typename T = Argument, typename = typename std::enable_if< IsVoid >::type >
void invoke() {
// only available for void class specialization
}
}
Signal<void> sv;
Signal<int> si;
sv.invoke(); // OK
si.invoke(1); // OK
sv.invoke(1); // NOT OK
sv.invoke("s"); // NOT OK
si.invoke(); // NOT OK
si.invoke("s"); // NOT OK
You can find more about the usage of enable_if here: std::enable_if to conditionally compile a member function, Why should I avoid std::enable_if in function signatures.
What about C++11 variadic templates :
template<typename ...Args>
struct Signal {
typedef std::function<void(Args...)> slot_type;
std::list<slot_type> listeners;
template<typename Callable>
void connect(Callable callable) {
listeners.emplace_back(callable);
}
void invoke(Args... args) {
for(slot_type& slot : listeners)
slot(args...);
}
};
void show_me(const std::string& data, int id) {
std::cout << "hello " << data << " : " << id << std::endl;
}
int main() {
Signal<std::string, int> s;
s.connect(show_me);
s.invoke("world", 42);
// ...
}
It scales well with 0, 1 or more args.
If yo will try to declare a variable
void a;
you also will receive a compile error.
The problem is that compiler expects some type instead of Argument here
template <typename Argument> class Signal
{
void invoke(Argument arg) {}
};
and void is not treated as a type here.