C++ using function template in class template - c++

template <int C> class MyClass;
...
template <int C>
double trans(MyClass<C> &a)
{
//return some double
}
//this is supposed to be function template
template <int C>
double func(MyClass<2> &a)
{
//return some double
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
MyClass<C-1> ret;
return ret;
}
template <int C>
double otherFunc(MyClass<C> &a)
{
double result;
if(C == SOME_CONSTANT)
result = func(a);
else
result = trans(func(a));
}
What my problem is I want to check template argument C in parameter otherFunc call function template func (return double) instead of member function of class template func(return MyClass). But somehow compiler attempts to func that returns MyClass in
if(C == SOME_CONSTANT)
result = func(a);
this part so I got compiler error (because double = MyClass is not viable). How should I fix this problem?

Hard to elaborate in a comment, so I'll post an answer. To recap what I said in comments:
you can't have an if/else in otherFunc that won't be legal to compile for a specific MyClass instantiation,
you can use the same template specialisation approach to create an alternative otherFunc definition to handle MyClass<2>s.
Sample code:
#include <iostream>
template <int C>
class MyClass
{ };
template <int C>
double trans(const MyClass<C> &a)
{
return C + 700;
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
MyClass<C-1> ret;
return ret;
}
double func(MyClass<2>& a)
{
return 200.0;
}
template <int C>
double otherFunc(MyClass<C> &a)
{
return trans(func<C>(a));
}
template <>
double otherFunc<2>(MyClass<2>& a)
{
return func(a);
}
int main()
{
MyClass<2> a;
std::cout << otherFunc(a) << '\n';
MyClass<4> b;
std::cout << otherFunc(b) << '\n';
}
Output:
200
703

I guess the reason is that:
if(C == SOME_CONSTANT)
result = func(a);
else
result = trans(func(a));
is checked in the runtime, while template specializations and type check is done during compilation. Those 2 functions:
template <int C>
double func(MyClass<2> &a)
{
//return some double
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
MyClass<C-1> ret;
return ret;
}
have almost the same signatures - the only difference is that for C==2 is used the one returning double, otherwise used the one returning MyClass<2>. So what you get is:
// for C==2
if(C == SOME_CONSTANT)
result = func<2>(a); // returns double - just like you wanted
else
result = trans<2>(func<2>(a)); // pass double into trans<?>(MyClass<?>), so the parameter cannot be resolved unless you specified some implicit constructor/explicit conversion operator
// for e.g. C==3
if(C == SOME_CONSTANT)
result = func<3>(a); // returns MyClass<2> while result is double
else
result = trans<2>(func<3>(a)); // pass MyClass<2> into trans<?>(MyClass<>) - like you wanted
so basically your code have invalid type for specializations in either first or second case. You can do something like this instead:
template <>
MyClass<2> func<2>(MyClass<2> &a)
{
return a;
}
template <int C>
MyClass<C-1> func(MyClass<C> &a)
{
return MyClass<C-1>;
}
to keep templates type consistent, and then:
if(C == SOME_CONSTANT)
result = trans(a);
else
result = trans(func(a));
It's just an example how to handle it. Things should be easier if you avoid messing with different return typed for different template specializations.

Related

Using a template function with different returns for different types doesn't work

so I wrote something like that:
class MyClass
{
enum varType {INTEGER, DOUBLE, VECTOR};
int beautiful_integer;
double awesome_double;
std::vector<float> many_floats;
template <class T>
T getVariable(varType type)
{
if(type == INTEGER)
{
return beatiful_integer;
}
if(type == DOUBLE)
{
return awesome_double;
}
if(type == VECTOR)
{
return many_floats;
}
}
...
};
But my compiler throws error "In instantiation of ..." and basically tells me that the return types don't match (and lists all of the unmatched ones, except the right one) and then tries to instantiate it with another type (for example double) and tells me that the return type doesn't match with int and vector of floats.
What am I doing wrong and how to properly write a template function in order to return different types depeneding on the parameter it was called with. For example when I call:
MyClass some_class(); //EDIT: this should be MyClass some_class;
//thanks for pointing it out
int some_number = some_class.getVariable(INTEGER);
I want to assign the value of beautiful_integer to some_number
As alternative, with std:
template <class T>
const T& getVariable() const
{
return std::get<const T&>(std::tie(beautiful_integer, awesome_double, many_floats));
}
template <class T>
T& getVariable()
{
return std::get<T&>(std::tie(beautiful_integer, awesome_double, many_floats));
}
Template parameters are determined at compile time. You can accomplish it by template specializations. Don't use enum. e.g.
class MyClass
{
int beautiful_integer;
double awesome_double;
std::vector<float> many_floats;
template <class T>
T getVariable();
template<>
int getVariable<int>
{
return beatiful_integer;
}
template<>
double getVariable<double>
{
return awesome_double;
}
template<>
std::vector<float> getVariable<std::vector<float>>
{
return many_floats;
}
};
From C++17 you can also use Constexpr If, e.g.
template <class T>
T getVariable()
{
if constexpr (std::is_same_v<T, int>)
{
return beatiful_integer;
}
else if constexpr (std::is_same_v<T, double>)
{
return awesome_double;
}
else if constexpr (std::is_same_v<T, std::vector<float>>)
{
return many_floats;
}
else
{
...
}
}
then
MyClass some_class;
int some_number = some_class.getVariable<int>();
BTW: MyClass some_class(); doesn't do what you expect. See most vexing parse.

Templated function pointer with arguments as template parameter

I can't get my code to compile. I want to be able to declare a function that takes another function (or a class with operator() defined, or lambda expression) as parameter, and this function has template arguments as well. Here's an example of what I want to do:
template <class T, T F>
T execF(T a, T b)
{
return F(a, b);
}
template <class T>
T add(T a, T b)
{
return a + b;
}
int main()
{
std::cout << execF<int, add>(3,4) << std::endl;
}
Is it possible to do something like this in C++14?
It should be:
template <class T, T F(T, T)>
T execF(T a, T b)
{
return F(a, b);
}
If you want to omit all template parameters, I suggest to stick with Some Programmer Dude's comment and accept the function as a function parameter:
template <typename T>
T execF(T a, T b, T f(T, T))
{
return f(a, b);
}
template <typename T>
T add(T a, T b) { return a + b; }
int main(int , char **)
{
auto x = execF(41.8f, 0.2f, add); //all params deduced in both functions
std::cout << x << std::endl;
x = execF(27, 15, add); //idem
std::cout << x << std::endl;
return 0;
}

Convert overloaded functions to specialized function templates

I've a function that is currently overloaded for different data types and takes a lambda(function pointer) to initialize those data types. I'm in process of converting them to template instances but haven't been successful yet.
Here's the overloaded version -
#include <iostream>
using namespace std;
void doSome(int (*func)(int &)){
int a;
a = 5;
int res = func(a);
cout << a << "\n";
}
void doSome(int (*func)(double &)){
double a;
a = 5.2;
int res = func(a);
cout << a << "\n";
}
int main() {
doSome([](int &a){
a += 2;
return 1;
});
doSome([](double &a){
a += 2.5;
return 1;
});
return 0;
}
Note that I've taken example of int and double for simplification, they might be some entirely different(and complex) types in actual code.
Here's what I've tried yet -
#include <iostream>
using namespace std;
template <typename F, typename S>
void doSome(F &func){
S a;
auto res = func(a);
cout << res << "\n";
}
template<>
void doSome<typename F, int> (F &func){
int a;
a = 5;
auto res = func(a);
cout << res << "\n";
}
template<>
void dpSome<typename F, double> (F &func){
double a;
a = 5.5
auto res = func(a);
cout << res << "\n";
}
int main() {
doSome([](int &a){
a += 2;
return 1;
});
doSome([](double &a){
a += 2.5;
return 1;
});
return 0;
}
Also while invoking templated functions, if I don't have to pass <any type hints> to the function, that would be much better solution.
There are a few issues with your approach. First, you can't partially specialize function templates, so that's out from the gate. Second, you're taking your function by lvalue reference - which prevents you from passing in a lambda, which is a prvalue.
In this case, it's easy to just add some SFINAE on your function template so that one only participates in overload resolution if it can be called with int& and the other only with double&:
template <class F>
auto doSome(F f)
-> decltype(f(std::declval<int&>()), void())
{
// int& case
}
template <class F>
auto doSome(F f)
-> decltype(f(std::declval<double&>()), void())
{
// double& case
}
If you want to make a generic version of doSome(), which doesn't use SFINAE for overload resolution, it gets a bit more complex.
#include <type_traits> // For std::remove_reference_t.
namespace detail {
// Helper to isolate return and parameter types, for a single-parameter callable.
template<typename T>
struct isolate_types;
// Function.
template<typename R, typename P>
struct isolate_types<R(P)> { using Ret = R; using Param = P; };
// Function pointer.
template<typename R, typename P>
struct isolate_types<R(*)(P)> { using Ret = R; using Param = P; }
// Pointer-to-member-function. Used for lambdas & functors.
// Assumes const this pointer.
template<typename R, typename C, typename P>
struct isolate_types<R (C::*)(P) const> { using Ret = R; using Param = P; };
// Lambda. Uses lambda's operator().
// Credit goes to ecatmur: http://stackoverflow.com/a/13359520/5386374
template<typename T>
struct isolate_types : isolate_types<decltype(&std::remove_reference_t<T>::operator())> {};
// Individual type aliases.
template<typename T>
using IsolateReturn = typename isolate_types<T>::Ret;
template<typename T>
using IsolateParam = typename isolate_types<T>::Param;
// Internal values, used by doSome().
template<typename T> T value;
template<> constexpr int value<int> = 5;
template<> constexpr double value<double> = 5.2;
// Define others as needed...
} // namespace detail
template<typename F>
void doSome(F func) {
// Determine necessary types.
using Ret = detail::IsolateReturn<F>;
using Param = std::remove_reference_t<detail::IsolateParam<F>>;
// And voila.
Param a = detail::value<Param>;
Ret res = func(a); // Can also use auto, if Ret isn't needed elsewhere.
std::cout << a << "\n";
}
Plugging this into your code... and it works.
Note that I'm not sure if this will work with all lambdas as written, and that it currently won't work with references to functions. It's easy enough to extend, however, by adding additional specialisations of isolate_types.

Variadic class template

I have a problem compiling this code.
All I am trying to do is create a variadic class template that can give me the sum of all elements that are passed in (eg. 1,2,3,4,5,6 should give 21) whether it's int or float. I could basically do it with two function templates recursively for which I'm getting the answer correctly but when I'm implementing it in class it doesn't give me an answer.
template <typename T>
class Myclass
{
public:
T sum;
T func(T A,T... B)
{
sum+=A;
func(B...);
}
T func(T A)
{
sum+=A;
return sum;
}
};
int main()
{
Myclass<int> myclass;
cout<<myclass.func(12,11,11,23);
return 0;
}
Your code does not compile because T... is an invalid variadic expansion, as T is not a parameter pack.
You code has also several other issues. I will address them in the snippet below:
template <typename T>
class Myclass
{
public:
// `sum` needs to be initialized to a value, otherwise its value
// will be undefined.
T sum = 0;
// `TRest...` is a template variadic type pack.
template <typename... TRest>
T func(T A, TRest... B)
{
sum+=A;
// You need to return from the recursive case of `func`.
return func(B...);
}
T func(T A)
{
sum+=A;
return sum;
}
};
working wandbox example
Note that the values matched in TRest... can be of any type. If you want to force them to be T, you can use the following technique (or static_assert):
template <typename...>
using force_to_t = T;
// ...
T func(T A, force_to_t<TRest>... B)
{
sum+=A;
// You need to return from the recursive case of `func`.
return func(B...);
}
I learned this solution thanks to Piotr's answer on another question.
T func(T A,T... B)
{
sum+=A;
func(B...);
}
This is not valid C++ syntax when func is not a function template with T its template parameter pack. ... can only be used to expand packs; T is a non-pack template parameter of the class template.
There are two ways to do this, depending on what you want to achieve.
One: If you want func to accept a mix of arbitrary types, you can make it a (member) function template:
template <typename T>
class Myclass
{
public:
T sum;
template <class F1, class... F>
T func(F1 A, F... B)
{
sum+=A;
func(B...);
return sum;
}
template <class F>
T func(F A)
{
sum+=A;
return sum;
}
};
Two: If you want func to only accept Ts, you can change it to use an initializer list:
template <typename T>
class Myclass
{
public:
T sum;
T func(std::initializer_list<T> A)
{
for (const auto& a : A)
sum+=a;
return sum;
}
};
Note that this will require calling it with a list in braces (e.g. func({1, 2, 42}) instead of func(1, 2, 42), but it's the approach also taken by e.g. std::max.
Notice that there are a few issues in your code unrelated to the question at hand.
One, which I fixed in the example above, is that your first overload of func didn't return anything. Calling it would result in Undefined Behaviour.
Another one, pointed out by #Zereges in the comments, is that T sum is not initialised explicitly. If MyClass is instantiated with a POD type (such as int or double), it will be used uninitialised. You should add a constructor for Myclass:
Myclass() : sum{} {}
You can avoid the need for recursive calls by writing the sequential sum+=A as an initialiser for a std::initializer_list:
template <typename T>
class Myclass
{
template <class... F>
T func_impl(F... A)
{
std::initializer_list<int>{
((void)(sum+=A), 0)...};
return sum;
}
public:
Myclass() :sum(){}
T sum;
template <class F1, class... F>
T func(F1 A, F... B)
{
return func_impl(A, B...);
}
};
You can do that with another approach that avoid recursive call.
template <typename T>
class Myclass
{
public:
T sum;
Myclass() { sum = 0; }
template<typename ...T1>
T func(T1 ... args)
{
auto l_expansion_list =
{
(
[this, &args]()
{
this->sum += args;
return true;
}()
)...
};
return sum;
}
};

Conditional compilation based on template values?

the question posed in:
Type condition in template
is very similar, yet the original question wasn't quite answered.
#include "stdafx.h"
#include <type_traits>
class AA {
public:
double a;
double Plus(AA &b) {
return a + b.a;
}
};
template<class T> double doit(T &t) {
if (std::is_same<T, AA>::value)
return t.Plus(t);
else
return t + t;
}
int _tmain(int argc, _TCHAR* argv[])
{
double a;
AA aa;
doit(a);
doit(aa);
return 0;
}
This doesn't compile, nor did I expect it to. Is something like this possible? Being, based on the template value, I want some code to be compiled, and others not. Here, 'double' doesn't have a method called "Plus" and class "AA" doesn't override the '+' operator. Operator overloading isn't always desirable when considering subtle semantics to the operations, so I'm looking for an alternative. I'd prefer to do #ifdef's (truly conditional compilation as posed in the ref'd question), but based on template values.
Since C++17 there is static if which is called if-constexpr. The following compiles fine since clang-3.9.1 and gcc-7.1.0, latest MSVC compiler 19.11.25506 handles well too with an option /std:c++17.
template<class T> double doit(T &t) {
if constexpr (std::is_same_v<T, AA>)
return t.Plus(t);
else
return t + t;
}
What you want is a static if. C++ doesn't have it. There are many ways to work around it, none as good as native support. In addition to the methods specified in the other two answers, you could try tag dispatch.
template<class T> double doitImpl(T &t, std::true_type) {
return t.Plus(t);
}
template<class T> double doitImpl(T &t, std::false_type) {
return t+t;
}
template<class T> double doit(T &t) {
return doitImpl(t, std::is_same<T, AA>);
}
Overloading?
template<class T>
double doit(T &t) {
return t + t;
}
double doit(AA &t) {
return t.Plus(t);
}
Explicit specialization is also possible, though superfluous:
template<class T>
double doit(T &t) {
return t + t;
}
template<>
double doit<>(AA &t) {
return t.Plus(t);
}
double doit(AA &t) {
return t.Plus(t);;
}
template<class T> double doit(T &t) {
return t + t;
}
Your code doesn't work because if the template is deduced as AA then t + t inside the body is ill-formed. On the other hand if T is deduces as double then t.Plus(t) becomes ill-formed.
To better understand what is happening:
A template is instantiated for each template type is called with.
doIt(a) instantiates doIt with T = double:
double doIt<double>(double &t) {
if (false)
return t.Plus(t); // <-- syntax error
else
return t + t;
}
doIt(aa) instantiates doIt with T = AA:
double doIt<AA>(AA &t) {
if (true)
return t.Plus(t);
else
return t + t; // <-- syntax error
}
You should avoid specializing function templates because functions overload. You can read this excellent Herb Sutter article: Why Not Specialize Function Templates?
This code compiles and runs:
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/utility/enable_if.hpp>
using namespace boost;
class AA {
public:
double a;
AA Plus(AA &b) {
AA _a;
_a.a = a + b.a;
return _a;
}
};
template<class T>
typename disable_if<is_floating_point<T>, T >::type
doit(T &t) {
return t.Plus(t);
}
template<class T>
typename enable_if<is_floating_point<T>, T >::type
doit(T &t) {
return t+t;
}
int _tmain(int argc, _TCHAR* argv[])
{
double a;
AA aa;
doit(a);
doit(aa);
return 0;
}