I am trying to develop a class that has to be templated and needs to have some templated methods. I have been looking and, most likely because I do not know how to explain my problem, I have not been able to find the solution. Here is my exmaple:
template<typename T>
class TestClass{
public:
template<typename M>
TestClass(M val): val_(T(val)){}
template<typename N>
N func() {
return N(val_);
}
T get() {
return val_;
}
template<typename N>
N add(N val) {
return N(val_) + val;
}
private:
T val_;
};
This class will be called in templated functions such as this one:
template<typename T>
std::string str(TestClass<T> f)
{
std::ostringstream out;
out << f.func<T>();
out << "\n";
out << "get: ";
out << f.get();
out << "\n";
out << f.add<T>(0.0);
return out.str();
}
An here is an example of usage:
int main(int argc, char** argv){
TestClass<double> t('a');
std::cout<<"Manual output: \n";
std::cout<<"func: "<<t.func<double>()<<std::endl;
std::cout<<"get: "<<t.get()<<std::endl;
std::cout<<"add: "<<t.add<double>(0)<<std::endl;
std::cout<<"operator<< output: \n";
std::cout<<str(t)<<std::endl;
return 0;
}
I have compiled whithout std::string str(TestClass<T> f) function and its usage within the main and I observe the desired behaberou. However, I cannot compile this code with the following error:
error: expected primary-expression before '>' token
out << f.func<T>();
^
expected primary-expression before ')' token
out << f.func<T>();
^
expected primary-expression before '>' token
out << f.add<T>(0.0);
^
The compiler also produces errors regarding the << operator and the fact that f.func<T>() and f.add<T> type has not been resolved. If I remove the templated parts in the calls within str():
template<typename T>
std::string str(TestClass<T> f)
{
std::ostringstream out;
out << f.func();
out << "\n";
out << "get: ";
out << f.get();
out << "\n";
out << f.add(0.0);
return out.str();
}
Then, the compiler error is:
no matching function for call to 'TestClass<double>::func()'
out << f.func();
^
candidate is:template<class N> N TestClass<T>::func() [with N = N; T = double]
N func() {
^
couldn't deduce template parameter 'N'
out << f.func();
^
Which make sense because func() type can not be deduced. Also I have tryied, using f.func<T>() and f.add(0.0) but the error is simillar to the first one.
My question is: How should I do it so the compiler can do its job?
The func template member function must be labeled as a template function when called:
f.template func<T>();
The template keyword is required to indicate the left angle bracket < is NOT a less-than operator. See this explanation of this use of the template keyword.
The add member function picks up its template type from the parameter:
f.add(0.0); // typename N = double
Related
This question already has answers here:
Call function based on template argument type
(7 answers)
Closed 6 months ago.
Suppose that I have a couple of functions to process different types of arguments. For example processInt for processing int variables and processString for processing std::string variables.
int processInt(int i)
{
return i;
}
string processString(string s)
{
return s;
}
And, I have a template function called foo which takes either of int or std::string as argument. And inside this function I need to conditionally call the processInt or processString based on the variable type sent to it as the argument. The foo function would look like this:
#include <type_traits>
template<typename T>
T foo(T value)
{
T variable;
if (std::is_same<T, int>::value)
{
variable = processInt(value);
}
else if (std::is_same<T, string>::value)
{
variable = processString(value);
}
return variable;
}
int main() {
string s = "Abc";
int i = 123;
cout << foo(s) << " " << foo(i) << endl;
}
However, with the above foo function I get the following errors:
error: no matching function for call to 'processInt'
variable = processInt(value);
^~~~~~~~~~
note: in instantiation of function template specialization 'foo<std::__cxx11::basic_string<char> >' requested here
cout << foo(s) << " " << foo(i) << endl;
^
note: candidate function not viable: no known conversion from 'std::__cxx11::basic_string<char>' to 'int' for 1st argument
int processInt(int i)
^
error: no matching function for call to 'processString'
variable = processString(value);
^~~~~~~~~~~~~
note: in instantiation of function template specialization 'foo<int>' requested here
cout << foo(s) << " " << foo(i) << endl;
^
note: candidate function not viable: no known conversion from 'int' to 'std::__cxx11::string' (aka 'basic_string<char>') for 1st argument
string processString(string s)
^
Source code: https://godbolt.org/z/qro8991ds
How could I do this correctly to conditionally call functions based on type of the template parameter in generic functions?
EDIT
I like to use a single foo function without overloading or specializations as otherwise there may be some code duplications. The foo function may have lot of lines. But, the difference between code for int and string will be a single line.
For something like this I would suggest templates and specialization:
// Declare base template
template<typename T>
T process(T);
// Specialization for integers
template<>
int process(int value)
{
// ... code to process integers...
}
// Specialization for strings
template<>
std::string process(std::string value)
{
// ... code to process strings...
}
Then the foo function simply becomes
template<typename T>
T foo(T value)
{
return process(value);
}
All branches should be valid, even if branch is not taken.
C++17 has if constexpr which would solve your issue
template<typename T>
T foo(T value)
{
T variable;
if constexpr (std::is_same<T, int>::value)
{
variable = processInt(value);
}
else if constexpr (std::is_same<T, string>::value)
{
variable = processString(value);
}
return variable;
}
For pre-c++17, you might use overload to obtain similar results (tag dispatching for more complex cases):
int foo(int value)
{
return processInt(value);
}
std::string foo(const std::string& value)
{
return processString(value);
}
template <typename T
// possibly some SFINAE to allow some conversion with above functions
/*, std::enable_if_t<std::is_constructible<std::string, T>, bool> = true */>
T foo(T value)
{
return T{};
}
In C++17, you can use immediately-invoked lambda and if constexpr to do this
template<typename T>
T foo(T value) {
auto variable = [&value]{
if constexpr (std::is_same_v<T, int>)
return processInt(value);
else if constexpr (std::is_same_v<T, std::string>)
return processString(value);
}();
// use variable
return variable;
}
Demo
I want to make a simple logger which automatically runs a function and returns its value.
The class is defined as:
template <typename R, typename... Args>
class Logger3
{
Logger3(function<R(Args...)> func,
const string& name):
func{func},
name{name}
{}
R operator() (Args ...args)
{
cout << "Entering " << name << endl;
R result = func(args...);
cout << "Exiting " << name << endl;
return result;
}
function<R(Args...)> func;
string name;
};
I want to pass the following simple add function to the logger:
int add(int a, int b)
{
cout<<"Add two value"<<endl;
return a+b;
}
By calling it this way:
auto caller = Logger3<int(int,int)>(add,"test");
However, it generates the following errors:
error: function returning a function
133 | Logger3(function<R(Args...)> func,
| ^~~~~~~
decorator.h:138:7: error: function returning a function
138 | R operator() (Args ...args)
| ^~~~~~~~
decorator.h:145:26: error: function returning a function
145 | function<R(Args...)> func;
There are 3 issues in your code:
The Logger3 class template requires R to be the return value of the function (and Args it's arguments).
(R is not a function type as implied by your attempt to instantiate Logger3).
Therefore instantiating the Logger3 in your case of a function that gets 2 ints and returns an int should be:
auto caller = Logger3<int, int, int>(add, "test");
Your Logger3 constructor should be public in order to invoke it from outside the class.
For efficiency reasons, you should use std::forward to forward the arguments from operator() to your function. This will avoid copy of the arguments (more significant in cases where their types are more complex than ints).
Note that in order for std::forward to work as expected, operator() has to be itself a variadic template using forwarding references (see below).
Complete fixed version:
#include <string> // std::string
#include <functional> // std::function
#include <utility> // std::forward, std::declval
#include <iostream> // std::cout
template <typename R, typename... Args>
class Logger3
{
public:
Logger3(std::function<R(Args...)> func,
const std::string& name) :
func{ func },
name{ name }
{}
// Template with forwarding references to avoid copies
// 'typename' arg is for SFINAE, and only enables if a
// function accepting 'Args...' can evaluate with 'UArgs...'
template <typename...UArgs,
typename = decltype(std::declval<R(*)(Args...)>()(std::declval<UArgs>()...))>
R operator() (UArgs&&...args)
{
std::cout << "Entering " << name << std::endl;
R result = func(std::forward<UArgs>(args)...);
std::cout << "Exiting " << name << std::endl;
return result;
}
private:
std::function<R(Args...)> func;
std::string name;
};
int add(int a, int b)
{
std::cout << "Add two value" << std::endl;
return a + b;
}
int main()
{
auto caller = Logger3<int, int, int>(add, "test");
auto res = caller(3, 4);
std::cout << "result: " << res << std::endl;
return 0;
}
Output:
Entering test
Add two value
Exiting test
result: 7
Demo: Godbolt.
A side note: better to avoid using namespace std - see here: Why is "using namespace std;" considered bad practice?.
You need to use template class partial specialization to get the type of R.
template <typename F>
class Logger3;
template <typename R, typename... Args>
class Logger3<R(Args...)>
{
// implementation details
};
which makes int match the template parameter R of the partial specialization when you explicitly specify Logger3<int(int,int)>.
I am working on a problem that should (since its HW) be solved by variadic templates. My lack of understanding prevents me from solving following error.
The code is:
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
template<typename ... TL>
class LazySplitResult
{
public:
LazySplitResult(TL ...pl) :storedParams(pl ...) { }
void doStuff()
{
// invoke method with inside parameters
useStoredTuple(storedParams, std::index_sequence_for<TL...>());
}
template<typename T, typename T1, typename... Targs>
void doInsideLogic(T&& value1, Targs&& ... Fargs)
{
// there is some logic
// for example this
std::stringstream convert("0");
// it works for string,double,int ... other types are not supported
// so exception would be in place
convert >> value1;
}
void doInsideLogic()
{
}
private:
template<std::size_t... Is>
void useStoredTuple(const std::tuple<TL ...>& tuple,std::index_sequence<Is...>) {
wrapInsideLogic(std::get<Is>(tuple) ...);
}
void wrapInsideLogic(TL && ... args)
{
doInsideLogic(args...);
}
std::tuple<TL ...> storedParams;
};
template<typename ...TL>
LazySplitResult<TL ...> getResult(TL && ...pl)
{
return LazySplitResult<TL...>(pl ...);
}
int main()
{
std::string x;
int y;
double z;
// prepares an object
auto lazyResult=getResult(x, '.', '-', y, 'x', z , 'a');
// let it do its thing and set unset variables
lazyResult.doStuff();
std::cout << "x = " << x << ", y = " << y << ", z = " << z << std::endl;
return 0;
}
The error message is here
error C2664: 'void LazySplitResult<std::string &,char,char,int &,char,double &,char>::wrapInsideLogic(std::string &,char &&,char &&,int &,char &&,double &,char &&)': cannot convert argument 2 from 'const char' to 'char &&'
source_file.cpp(40): note: Conversion loses qualifiers
source_file.cpp(18): note: see reference to function template instantiation 'void LazySplitResult<std::string &,char,char,int &,char,double &,char>::useStoredTuple<0,1,2,3,4,5,6>(const std::tuple<std::string &,char,char,int &,char,double &,char> &,std::integer_sequence<_Ty,0,1,2,3,4,5,6>)'
It is copied from rextester here.
The main part of the HW is to parse a formula and save the result in the variables like this:
// declare variables and initialize class with formula
parse(x, '.', '-', y, 'x', z , 'a');
std::cout << "x = " << x << ", y = " << y << ", z = " << z << std::endl;
The code does the same, except it does it in a lazy fashion so the parameters needs to be stored in a tuple for later use.
I tried to implement the same logic, except the laziness, without employing tuples and class with a success. It can be observed here. No error describind conversion errors was raised.
Can you please help me? I am completely lost. The entry method invocation is the same, the variadic template method that process the parameters have the same arguments... Only difference seems to be the tuple and class inbetween.
First, there is a useless template parameter T1 in your doInsideLogic function template. Remove it.
The parameter of wrapInsideLogic is not a forwarding reference since TL is known and is not deduced. Its argument std::get<Is>(tuple) is of type const TL& since tuple is of type const std::tuple<TL...>&, thus does not match the parameter type TL&&. To fix this, there are two choices:
Use forwarding reference, i.e.
template <typename... Targs>
void wrapInsideLogic(Targs&&... args)
{
doInsideLogic(std::forward<Targs>(args)...);
}
Use reference to const, i.e.
void wrapInsideLogic(const TL&... args)
{
doInsideLogic(args...);
}
Not sure... do you ask help for the code referred by first link or for the code referred by the second link?
I suppose is the second code so I suggest to reorder the definition of functions as follows.
First the ground case of recursion
void myFunc ()
{ std::cout << "end" << std::endl; }
Second, the recursive version of myFunc()
template <typename T, typename ... Targs>
void myFunc (T && value1, Targs && ... Fargs)
{
// there should be additional logic
// if value1 is char then skip it. But it was intentionally ommited
std::cout << " do" << std::endl;
const char* parsedValue = "0";
std::stringstream convert(parsedValue);
// cast current value according to thing.
convert >> value1;
myFunc(Fargs ...);
}
Third, test()
template <typename ... Targs>
void test (Targs && ... args)
{ myFunc(args...); }
Now your code should compile.
If you write first test(), then the recursive myFunc() and last the ground case myFunc() you have that (1) test() call myFunc() when is undeclared and (2) the recursive myFunc() call the ground case myFunc() when is undeclared.
I am trying to write a simple template that I can use for memoization with functions taking a single argument:
#include <map>
template <typename F,typename OUT,typename IN>
OUT memoization(IN in){
static std::map<IN,OUT> memo;
static typename std::map<IN,OUT>::iterator found = memo.find(in);
if (found != memo.end()) { return found->second; }
OUT res = F(in);
memo(in) = res;
return res;
}
double test(double x) { return x*x; }
int main(){
for (int i=0;i<5;i++){
memoization<test,double,double>(i*0.5);
}
}
But i get the error:
error: no matching function for call to 'memoization(double)'
note: candidate is:
note: template OUT memoization(IN)
note: template argument deduction/substitution failed:
Why does this fail to compile?
Actually I dont understand why template argument deduction/substitution is taking place at all when I specify all the template parameters.
I am using gcc version 4.7.2 (no C++11 enabled)
PS: the template has many more errors than I first realized, but I leave it as is...
Your function template takes three type arguments:
template <typename F,typename OUT,typename IN>
OUT memoization(IN in) { ... }
You're passing in test for F. test isn't a type, it's a value. Also, the expression F(in) in your function template is wrong for the same reason.
This approach in general is pretty flawed as it seems pretty backwards from what actually is going on. Namely, it's the function that's being memoized, not a value. Also requiring the function value at compile time is pretty limiting.
A better approach is to treat memoization as a decorator. That is:
template <class F>
Memoized<F> memoize(F f) {
return {f};
}
such that:
auto memo_test = memoize(test);
memo_test(0); // performs computation
memo_test(0); // doesn't perform computation
memo_test(0); // ditto
I leave the implementation of Memoized<T> as an exercise.
Why does template argument deduction/substitution fail here?
a. Because there are 3 template arguments and only one actual argument, so two of them are undeduce-able (is that a word?).
b. There is a syntax error. Template argument F is a type, not a callable object.
If this has to work in a pre-c++11 environment, boost's result_of can help:
#include <map>
#include <boost/utility/result_of.hpp>
//
// now that template arguments are all used when collecting function
// arguments, the types F and IN can be deduced.
//
template <typename F,typename IN>
typename boost::result_of<F(IN)>::type memoization(F f, IN in)
{
typedef typename boost::result_of<F(IN)>::type OUT;
static std::map<IN,OUT> memo;
static typename std::map<IN,OUT>::iterator found = memo.find(in);
if (found != memo.end()) { return found->second; }
OUT res = f(in);
memo[in] = res;
return res;
}
double test(double x) { return x*x; }
int main(){
for (int i=0;i<5;i++){
memoization(test, i*0.5);
}
}
The answer already got a satisfactory answers, however I was curious if I could get it working with pre C++11. Actually it is possible to pass a function pointer as template parameter, one just has to specify this on the template parameter instead of letting it expect a type parameter:
#include <iostream>
#include <map>
using namespace std;
template <class T, class R, R (*Func)(T)>
R memoized(T in) {
static std::map<T,R> memo;
typename std::map<T,R>::iterator found = memo.find(in);
if (found != memo.end()) { return found->second; }
std::cout << "not found" << std::endl;
R res = Func(in);
memo[in] = res;
return res;
}
double test(double x){return x*x;}
double test2(double x){return x;}
int main() {
std::cout << memoized<double,double,test>(1) << std::endl;
std::cout << memoized<double,double,test>(1) << std::endl;
std::cout << memoized<double,double,test>(1) << std::endl;
std::cout << std::endl;
std::cout << memoized<double,double,test2>(1) << std::endl;
std::cout << memoized<double,double,test2>(1) << std::endl;
std::cout << memoized<double,double,test2>(1) << std::endl;
return 0;
}
output:
not found
1
1
1
not found
1
1
1
Still not sure if this is a good approach, but it seems to work.
I would like to store a std::function in a class as a member.
I have troubles with below test code when calling a.callMethod() where the method has been set just before with a.setMethod(). The code works fine if I remove the template.
I have tried to debug with a function callMethodOutsideClass without success.
Is there a better way to manage that ?
#include <iostream>
#include <vector>
#include <functional>
template<typename T>
struct A
{
A(size_t size, T value) : vec_(size, value), method_(nullptr) {}
void setMethod(const std::function<int(A<T>&)> method) { method_ = method; }
int callMethod()
{
if(method_)
return method_(*this);
else
std::cerr << "method not initialized" << std::endl;
return -1;
}
std::vector<int> vec_;
std::function<int(A<T>& a)> method_;
};
template<typename T>
int callMethodOutsideClass(struct A<T>& a, const std::function<int(A<T>&)> method)
{
return method(a);
}
template<typename T>
int apple(struct A<T>& a)
{
a.vec_[0] += 1;
return 1;
}
template<typename T>
int orange(struct A<T>& a)
{
a.vec_[0] += 2;
return 2;
}
int main()
{
A<int> a(10,4), b(10,4);
std::cout << callMethodOutsideClass(a, &apple) << std::endl;
a.setMethod(&orange);
std::cout << a.callMethod() << std::endl;
std::cout << a.vec_[0] << std::endl;
}
I currently get the following errors :
Foo6.cpp: In function ‘int main()’:
Foo6.cpp:46:47: error: cannot resolve overloaded function ‘apple’ based on conversion to type ‘std::function<int(A<int>&)>’
std::cout << callMethodOutsideClass(a, &apple) << std::endl;
^
Foo6.cpp:48:21: error: no matching function for call to ‘A<int>::setMethod(<unresolved overloaded function type>)’
a.setMethod(&orange);
^
Foo6.cpp:48:21: note: candidate is:
Foo6.cpp:9:7: note: void A<T>::setMethod(std::function<int(A<T>&)>) [with T = int]
void setMethod(const std::function<int(A<T>&)> method) { method_ = method; }
^
Foo6.cpp:9:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘std::function<int(A<int>&)>’
A pointer to function is not a std::function<T>. The std::function<T> signature can't be deduced based on the function address given as an argument. In addition, the compiler can't resolve a proper function template specialization to get its address when a conversion to std::function<T> is requested, since the constructor of std::function<T> is a function template as well.
You need to be more explicit:
std::cout << callMethodOutsideClass<int>(a, &apple<int>) << std::endl;
// ^^^^^ ^^^^^
a.setMethod(&orange<int>);
// ^^^^^
Is there any way to deduce templates parameters "easily" ?
You can modify the signature of callMethodOutsideClass in one of two ways:
Option #1:
Disable a type deduction on a std::function<int(A<T>&)> parameter:
template <typename T> struct identity { using type = T; };
template<typename T>
int callMethodOutsideClass(A<T>& a, const typename identity<std::function<int(A<T>&)>>::type method)
{
return method(a);
}
But you'll have to pay for the type-erasure applied by a std::function.
Option #2:
Let the compiler deduce the real type of a functor object given as an argument:
template <typename T, typename F>
int callMethodOutsideClass(A<T>& a, F&& method)
{
return std::forward<F>(method)(a);
}
In both cases you can just say:
callMethodOutsideClass(a, &apple<int>);
// ^^^^^
Note: You still have to pass the address of a concrete function template specialization by providing a list of template arguments &apple<int>. If you want to get away with a simple &address syntax, then the function taking it needs to declare an exact type of that argument:
template<typename T>
int callMethodOutsideClass(A<T>& a, int(*method)(A<T>&))
{
return method(a);
}
callMethodOutsideClass(a, &apple);
or you could help the compiler resolve the proper overload at a call site:
callMethodOutsideClass(a, static_cast<int(*)(decltype(a)&)>(&apple));
...or, you can use a lambda expression defined as follows:
template<typename T, typename F>
int callMethodOutsideClass(struct A<T>& a, F&& method)
{
return std::forward<F>(method)(a);
}
// in C++11:
callMethodOutsideClass(a, [](decltype(a)& x){return apple(x);});
// in C++14:
callMethodOutsideClass(a, [](auto&& x){return apple(std::forward<decltype(x)>(x));});
As far as the setMethod member function is concerned, the things are easier, since the compiler knows exactly that it expects const std::function<int(A<T>&)> method where T is known (not deduced). So basically, you just need to help the compiler to get the address of a function template specialzation you need at the call site:
a.setMethod(&orange<int>);
a.setMethod(static_cast<int(*)(decltype(a)&)>(&orange));