I've already asked a similar question here: Why does automatic type deduction fail?
However, I think I stripped down the code example too much in order to make a minimal working example. It does not reflect the problem I am facing.
Consider the code below. Why is it needed to add <int, std::string> as template arguments? Shouldn't the compiler be able to deduce this from fn_myfunc itself?
#include <functional>
#include <string>
template <class In, class Out>
class MyClass{
public:
MyClass(
std::function<Out (In)> process
) :
process_(process)
{
}
private:
std::function<Out (In)> process_;
};
std::string myfunc(int in) {
return std::to_string(in);
}
int main(int argc, char * * argv) {
std::function<std::string (int)> fn_myfunc = myfunc;
MyClass<int, std::string> instance(fn_myfunc);
return 0;
}
Related
I have begun a project that makes heavy use of c++20 concepts as a way of learning some of the new c++20 features. As part of it, I have a function template that takes a single argument and operates on it. I wish to have the flexibility to pass types to this function that specify another type that they operate on, but that defaults to something else if that specification doesn't exist.
For example, here is a type that specifies a type that it operates on:
struct has_typedef_t
{
typedef int my_type; //operates on this type
void use_data(const my_type& data) const
{
//do something else
}
};
and one that does not specify a type, but operates on a default type specified elsewhere:
typedef std::string default_type;
struct has_no_typedef_t
{
void use_data(const default_type& data) const
{
//do something
}
};
I have a simple concept that can tell me if any given type has this specification:
template <class T> concept has_type = requires(T t) {typename T::my_type;};
And the function might look something like the following:
template <class T> void my_function(const T& t)
{
//Here I want a default-constructed value of the default
//type if the argument doesn't have a typedef
typename std::conditional<has_type<T>, typename T::my_type, default_type>::type input_data;
t.use_data(input_data);
}
The problem here is that the second template argument is invalid for anything that doesn't specify a type, e.g. has_no_typedef_t. An example program:
int main(int argc, char** argv)
{
has_no_typedef_t s1;
has_typedef_t s2;
// my_function(s1); // doesn't compile:
//'has_no_typedef_t has no type named 'my_type'
my_function(s2); //compiles
return 0;
}
What I am looking for is a replacement for the following line:
typename std::conditional<has_type<T>, typename T::my_type, default_type>::type input_data;
as this is causing the problem. I am aware that I can pull of some tricks using an overloaded function using the concept above and combining decltype and declval, but I am looking for something clean and have not been able to come up with anything. How can I achieve this behavior cleanly?
Below is the full code for the full picture (c++20):
#include <iostream>
#include <type_traits>
#include <concepts>
#include <string>
template <class T> concept has_type = requires(T t) {typename T::my_type;};
struct has_typedef_t
{
typedef int my_type;
void use_data(const my_type& data) const
{
//do something
}
};
typedef std::string default_type;
struct has_no_typedef_t
{
void use_data(const default_type& data) const
{
//do something else
}
};
template <class T> void my_function(const T& t)
{
//Here I want a default-constructed value of the default
//type if the argument doesn't have a typedef
typename std::conditional<has_type<T>, typename T::my_type, default_type>::type input_data;
t.use_data(input_data);
}
int main(int argc, char** argv)
{
has_no_typedef_t s1;
has_typedef_t s2;
// my_function(s1); // doesn't compile:
//'has_no_typedef_t has no type named 'my_type'
my_function(s2); //compiles
return 0;
}
This has had a solution since C++98: a traits class. Concepts just makes it a bit easier to implement:
template<typename T>
struct traits
{
using type = default_type;
};
template<has_type T>
struct traits<T>
{
using type = T::my_type;
};
Without concepts, you'd need to use SFINAE to turn on/off the specializations based on whether the type has the trait or not.
You can use lambda combined with if constexpr to determine the type of the return. The type_identity here is to solve the problem that the type may not be default_initializable.
typename decltype([]{
if constexpr (requires { typename T::my_type; })
return std::type_identity<typename T::my_type>{};
else
return std::type_identity<default_type>{};
}())::type input_data;
t.use_data(input_data);
Demo
Cant understand what is wrogn with code, second function definition or call of this function in main?
I think, but not sure, problem in call, cause without calling code compiles well. Compiler gcc
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<class T>
void show_element(T ob)
{
cout << ob << " ";
}
template<template<class> class S, class T>
void show_sequence(S<T> sequence)
{
for_each(sequence.begin(), sequence.end(), show_element<T>);
}
int main(int argc, char const *argv[])
{
std::vector<int> v(20, 0);
//here the problem
show_sequence<std::vector<int>, int>(v);
return 0;
}
std::vector isn't a template of one parameter, it takes an allocator type as well. You can use it as vector<T> simply because the second parameter has a default (std::allocator<T>).
As it's written, your template function cannot accept any standard container, since off the top of my head, none take just a single type parameter.
An approach that would work, and not require you to know how many template parameters a container requires, is to accept a container type (not template), and glean the value type from the container type.
template<class Seq>
void show_sequence(Seq const& sequence)
{
typedef typename Seq::value_type T;
for_each(sequence.begin(), sequence.end(), show_element<T>);
}
All standard containers have a value_type member, so this will work with any of them. Furthermore, it will work with any container that takes its cue from the standard library.
The problem is that std::vector is a template but std::vector<int> is a type.
When you are giving the second one to the function, you are giving one type and not a template.
So, you can rewrite your function as :
template<class S>
void show_sequence(S sequence)
Moreover, vector does not take only one template paramete but two (see StoryTeller answer)
It is similar to this question: https://stackoverflow.com/a/29493191/1889040
It is because vector is template of <type, allocator>
The code should be
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<class T>
void show_element(T ob)
{
cout << ob << " ";
}
template<template<class,class> class S, class T, class Allocator>
void show_sequence(S<T, Allocator> sequence)
{
for_each(sequence.begin(), sequence.end(), show_element<T>);
}
int main(int argc, char const *argv[])
{
std::vector<int> v(20, 0);
//here problem solved
show_sequence<vector, int, allocator<int> > (v);
show_sequence(v);
return 0;
}
I asked a question about using a lambda to achieve something similar earlier but wasn't able to get this working so I've tried approach the problem using a functor instead. This is probably neater anyway in the sense that it doesn't involve constructing std::function objects and is closer to the example case set out in the documentation.
Here is a simple setup that illustrates my problem:
#include <boost/range/adaptor/transformed.hpp>
#include <vector>
// some structure
template <typename T>
struct MyStruct
{
MyStruct(T t)
{
}
};
template <typename T>
struct Converter
{
using return_type = MyStruct<T>;
return_type operator()(const T& value)
{
return return_type(value);
}
};
int main(int argc, const char* argv[])
{
std::vector<int> vec {1, 2, 3};
auto val = vec | boost::adaptors::transformed(Converter<int>());
return 0;
}
When I try to compile this I am getting the following error message:
/home/user/packages/boost/mpl/eval_if.hpp:38:31: error: no type named
‘type’ in ‘boost::mpl::eval_if<boost::is_same<boost::use_default,
boost::use_default>, boost::result_of<const Converter<int>(int&)>,
boost::mpl::identity<boost::use_default> >::f_ {aka struct
boost::result_of<const Converter<int>(int&)>}’
I'm not sure what to make of this. I can't spot any errors in the code. Any ideas?
The error tells you boost::result_of<const Converter<int>(int&)> doesn't have the type member. In other words, the function call operator doesn't work when a const Converter<int> object is used. Once you know the problem, it's easy to see what's wrong:
return_type operator()(const T& value) const
// ^^^^^
{
return return_type(value);
}
I am trying with the following code.
#include <iostream>
#include <vector>
using namespace std;
template <typename T, std::vector <T> myV>
int fun()
{
cout <<" Inside fun () "<<endl;
}
int main( int argc, char ** argv)
{
std::vector<int> a;
fun<int,a>();
}
I can not pass std::vector myV ?
But instead of std::vector , i could able to use something like template
**, and fun().
What goes in triangular brackets must be a type or a compile-time constant; it cannot be a variable. Although a's type is vector<int>, a itself is an object of type vector<int>; it cannot go in triangular brackets as one of template parameters.
In addition, you cannot use vector<T> as a second type parameter of your template function: vector<T> is a type which becomes fully known once you know T. Since you already have T as your template parameter, you can declare vector<T> "for free" inside your function without an additional template parameter.
Finally, in C++11 you can get a type of a variable statically using decltype. For example, if you modify your code to take a single type parameter of T, you could do this:
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
int fun()
{
cout <<" Inside fun () "<<endl;
}
int main( int argc, char ** argv)
{
std::vector<int> a;
// This is the same as calling fun<std::vector<int> >();
fun<decltype(a)>();
return 0;
}
Demo on ideone.
You need to call
fun<int, std::vector<int>>();
which passes the type std::vector<int>. If you want to pass an instance (a) of std::vector<int>, you will have to change your function definition to:
template<typename T>
int fun(std::vector<T> v)
{
std::cout << "Inside fun()\n";
}
int main()
{
std::vector<int> a;
fun(a);
}
I am confused about the strange syntax provided by C++ function templates and class templates. Take a quick look at the code below:
#include <iostream>
#include <algorithm>
#include <functional>
#include <iterator>
#include <vector>
using namespace std;
template <class op1,class op2>
class compose_fg_x_t : public unary_function<typename op2::argument_type,typename op1::result_type>{
public:
// constructor
compose_fg_x_t(const op1& arg1,const op2& arg2): p1(arg1),p2(arg2){
}
//function call
typename op1::result_type operator()(const typename op2::argument_type& x) const{
return p1(p2(x));
}
private:
op1 p1;
op2 p2;
};
template <class Op1,class Op2>
inline compose_fg_x_t<Op1,Op2> compose_fg_x(const Op1& p1,const Op2& p2){
return compose_fg_x_t<Op1,Op2>(p1,p2);
}
int main(int argc, char *argv[])
{
int a[] = {1,2,3,4,5};
vector<int> IntVec(a,a+sizeof(a)/sizeof(int));
copy(IntVec.begin(),IntVec.end(),ostream_iterator<int>(cout," "));
cout<<endl;
transform(IntVec.begin(),IntVec.end(),ostream_iterator<int>(cout," "),compose_fg_x( bind2nd(multiplies<int>(),5),bind2nd(plus<int>(),10) ));
transform(IntVec.begin(),IntVec.end(),ostream_iterator<int>(cout," "),compose_fg_x_t( bind2nd(multiplies<int>(),5),bind2nd(plus<int>(),10) ));
return 0;
}
So, my question is, why is the first transform is correct while the second is not? What the helper function compose_fg_x does is return an object of the underlying compose_fg_x_t type. I am trying to omit the indirect function call, which fails to compile. Why?
Template arguments can only be deduced for function templates, not for class templates. The whole point of helper functions such as make_pair (or your compose_fg_x) is to circumvent this limitation.
Here is a slightly less complicated example that demonstrates the problem:
#include <utility>
int main()
{
auto x = std::make_pair(3, 4); // function template arguments are deduced
auto y = std::pair(3, 4); // error: missing class template arguments
}