Related
template<typename T, size_t M, size_t K, size_t N, typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0>
void fastor2d(){//...}
I copied this line of code from cpp-reference(only the std::enable_if part, i do need T and all three of the size_t's), because i would like to use this function only when floating_types are used on it ... it does not compile.
Could somebody explain to me, why, and what it even does? While i am at it, how do you call this function afterwards?
Every tutorial or question here on SO gets bombed with answers, and that is great, but to someone who does not understand jacks*** of what is happening, even those are not really helpful.(sry, if possibly slightly agitated or aggressive)
EDIT: i greatly appreciate all answers as of now, i realize that my wording might have been a bit off ... i understand what a template parameter is, and know the difference between runtime and compiletime etc, but i just cant get a good grasp of the syntax behind std::enable_if
EDIT2:
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_integral<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.randInt();
}
This is literally the only thing i need changed. Notice the random() part
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.random();
}
I'll try to explain this as simple as possible not to go into the language details too much since you asked for it.
Template arguments are compile time arguments (they do not change during the run-time of your application). Function arguments are run-time and have a memory address.
Calling this function would look something like this:
fastor2d<Object, 1, 2, 3>();
In the <> brackets you see the compile-time arguments or more accurately the template parameters, and the function in this case takes 0 runtime arguments in the () brackets. The last compile time argument has a default argument which is used to check whether the function should compile at all (enable_if type). If you want to know more clearly what enable if does you should search for the term SFINAE, which is a template metaprogramming technique used to determine whether a function or class should exist or not.
Here is a short SFINAE example:
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void function(T arg)
{
}
function(0.3f); //OK
function(0.0); //OK double results in std::is_floating_point<double>::value == true
function("Hello"); //Does not exist (T is not floating point)
The reason the third function call fails, is because the function does not exist. This is because the enable if caused the function not to exist when the compile-time bool that is passed in as its' template argument is false.
std::is_floating_point<std::string>::value == false
Do note that a lot of people agree that the SFINAE syntax is horrible and that a lot of SFINAE code will not be necessary anymore with the introduction of concepts and constraints in C++ 20.
Rather than a top-down approach starting with you code snippet, I'll take a bottom-up approach to explain some important details about templates and what tools and techniques are involved.
At heart, templates are a tool that let you write C++ code that applies to a range of possible types, not strictly for a fixed type. In a statically-typed language, this is firstly a great tool for reusing code without sacrificing type safety, but in C++ in particular, templates are very powerful because they can be specialized.
Every template declaration begins with the keyword template, and a list of type or non-type (i.e value) parameters. Type parameters use the special keyword typename or class, and are used to let your code work over a range of types. Non-type parameters simply use the name of an existing type, and these let you apply your code to a range of values that are known at compile-time.
A very basic templated function might look like the following:
template<typename T> // declare a template accepting a single type T
void print(T t){ // print accepts a T and returns void
std::cout << t; // we can't know what this means until the point where T is known
}
This lets us reuse code safely for a range of possible types, and we can use it as follows:
int i = 3;
double d = 3.14159;
std::string s = "Hello, world!";
print<int>(i);
print<double>(d);
print<std::string>(s);
The compiler is even smart enough to deduce the template parameter T for each of these, so you can safely get away with the following, functionally identical code:
print(i);
print(d);
print(s);
But suppose you want print to behave differently for one type. Suppose, for example, you have a custom Point2D class that needs special handling. You can do this with a template specialization:
template<> // this begins a (full) template specialization
void print<Point2D>(Point2D p){ // we are specializing the existing template print with T=Point2D
std::cout << '(' << p.x << ',' << p.y << ')';
}
Now, anytime we use print with T=Point2D, the specialization is chosen. This is really useful, for example, if the generic template just doesn't make sense for one specific type.
std::string s = "hello";
Point2D p {0.5, 2.7};
print(s); // > hello
print(p); // > (0.5,2.7)
But what if we want to specialize a template for many types at once, based on a simple condition? This is where things become a little meta. First, let's try to express a condition in a way that lets them be used inside templates. This can be a little tricky because we need compile-time answers.
The condition here will be that T is a floating point number, which is true if T=float or T=double and false otherwise. This is actually fairly simple to achieve with template specialization alone.
// the default implementation of is_floating_point<T> has a static member that is always false
template<typename T>
struct is_floating_point {
static constexpr bool value = false;
};
// the specialization is_floating_point<float> has a static member that is always true
template<>
struct is_floating_point<float> {
static constexpr bool value = true;
};
// the specialization is_floating_point<double> has a static member that is always true
template<>
struct is_floating_point<double> {
static constexpr bool value = true;
}
Now, we can query any type to see if it's a floating point number:
is_floating_point<std::string>::value == false;
is_floating_point<int>::value == false;
is_floating_point<float>::value == true;
is_floating_point<double>::value == true;
But how can we use this compile-time condition inside another template? How can we tell the compiler which template to choose when there are many possible template specializations to choose from?
This is achieved by taking advantage of a C++ rule called SFINAE, which in basic English, says, "when there are many possible template specializations to choose from, and the current one doesn't make sense*, just skip it and try the next one."
There's a list of errors, when attempting to substitute template arguments into templated code, that cause the template to be ignored without an immediate compiler error. The list is a bit long and complex.
One possible way that a template doesn't make sense is if it tries to use a type that doesn't exist.
template<typename T>
void foo(typename T::nested_type x); // SFINAE error if T does not contain nested_type
This is the exact same trick that std::enable_if uses under the hood. enable_if is a template class accepting a type T and a bool condition, and it contains a nested type type equal to T only when the condition is true. This is also pretty easy to achieve:
template<bool condition, typename T>
struct enable_if {
// no nested type!
};
template<typename T> // partial specialization for condition=true but any T
struct enable_if<true, T> {
typedef T type; // only exists when condition=true
};
Now we have a helper that we can use in place of any type. If the condition we pass is true, then we can safely use the nested type. If the condition we pass is false, then the template is no longer considered.
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type // This is the return type!
numberFunction(T t){
std::cout << "T is a floating point";
}
template<typename T>
typename std::enable_if<!std::is_floating_point<T>::value, void>::type
numberFunction(T t){
std::cout << "T is not a floating point";
}
I completely agree that std::enable_if<std::is_floating_point<T>::value, void>::type is a messy way to spell out a type. You can read it as "void if T is floating point, and otherwise stop and try the next overload"
Finally, to take apart your example:
// we are declaring a template
template<
typename T, // that accepts some type T,
size_t M, // a size_t M,
size_t K, // a size_t K,
size_t N, // a size_t N,
// and an unnamed non-type that only makes sense when T is a floating point
typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0
>
void fastor2d(){//...}
Note the = 0 at the end. That's simply a default value for the final template parameter, and it lets you get away with specifying T, M, K, and N but not the fifth parameter. The enable_if used here means that you can provide other templates called fastor2d, with their own sets of conditions.
First of all, I'll rewrite your function in a working form
template <typename T, size_t M, size_t K, size_t N,
std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
void fastor2d() // ..........................................^^^ int, not T
{ }
The point is that I've changed the second template argument of std::enable_if_t form T to int.
I've also removed the typename before std::enable_if_t but isn't important: the typename is implicit in the _t at the end of std::enable_if_t, introduced from C++14. In C++11 the correct form is
// C++11 version
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0
// ^^^^^^^^ no _t ^^^^^^
But why it works?
Start from the name: SFINAE.
Is a short form for "Substitution Failure Is Not An Error".
It's a C++ rule so that when you write some thing as
template <int I, std::enable_if_t< I == 3, int> = 0>
void foo ()
{ }
and I is 3, the condition of std::enable_if_t is true so std::enable_if_t< I == 3, int> is substituted with int so foo() is enabled but when I isn't 3, the condition of std::enable_if_t if false so std::enable_if_t< I == 3, int> is not substituted so foo() isn't enabled but this ins't an error (if, through overloading, there is another foo() function, enabled, that matches the call, obviously).
So where is the problem in your code?
The problem is that std::enable_if_t is substituted, when the first template parameter is true, with the second parameter.
So if you write
std::enable_if_t<std::is_floating_point<T>::value, T> = 0
and you call
fastor2d<float, 0u, 1u, 2u>();
the std::is_floating_point<float>::value (but you can also use the shorter form std::is_floating_point_v<T> (_v and not ::value)) so the substitution take place and you get
float = 0
but, unfortunately, a template value (not type) parameter can't be of type floating point, so you get an error.
If you use int instead of T, the substitution give you
int = 0
and this is correct.
Another solution can be use the following form
typename = std::enable_if_t<std::is_floating_point<T>::value, T>
as suggested by Andreas Loanjoe, because the substitution give you
typename = float
that is a valid syntax.
But this solution has the drawback that doesn't works when you want to write two alternative functions, as in the following example
// the following solution doesn't works
template <typename T,
typename = std::enable_if_t<true == std::is_floating_point<T>::value, int>>
void foo ()
{ }
template <typename T,
typename = std::enable_if_t<false == std::is_floating_point<T>::value, int>>
void foo ()
{ }
where works the solution based on the value
// the following works
template <typename T,
std::enable_if_t<true == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
template <typename T,
std::enable_if_t<false == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
I am writing template function that is parametrized by single type, and has variable number of parameters of the same type (not of different types). It should check if first value is among the rest. I wanted to write it like this:
#include <unordered_set>
template <typename T>
static bool value_in(T val, T vals...) {
// compiles, but uses only vals[0]:
const std::unordered_set<T> allowed {vals};
// error: pack expansion does not contain any unexpanded parameter packs:
// const std::unordered_set<T> allowed {vals...};
return allowed.find(val) != allowed.end();
}
// usage
enum class Enumeration {one, two, three};
int main () {
// should return true -> 0
return value_in(Enumeration::two,
Enumeration::one,
Enumeration::two) ? 0 : 1;
}
I expected that second to work, but it doesn't compile because
test.cpp: In function ‘bool value_in(T, T, ...)’:
test.cpp:7:46: error: expansion pattern ‘vals’ contains no argument packs
I see the "(T, T, ...)" instead of "(T, T...)", so probably I messed up function declaration and ended with C-style variadic function.
How to write declaration that will accept arbitrary number of parameters of the same type?
First of all, defining a C-style variadic function
static bool value_in (T val, T vals, ...)
the comma before the ... is optional.
So your
static bool value_in(T val, T vals...)
define two not-variadic arguments (val and vals) and an unnamed variadic sequence.
How to write declaration that will accept arbitrary number of parameters of the same type?
There are many ways but, IMHO, with drawbacks
A possible way is the use of SFINAE: you can impose that the variadic types are equal to the first type.
The following is a C++17 possible solution that uses template folding
template <typename T, typename ... Ts>
std::enable_if_t<(std::is_same<T, Ts>::value && ...), bool>
value_in (T val, Ts ... vals)
{
const std::unordered_set<T> allowed {val, vals ... };
return allowed.find(val) != allowed.end();
}
You can develop this solution also in C++11/C++14 but is a little more complicated.
Drawback: the Ts... type are deduced and they must be exactly the same T type.
So if you want, by example, a function that accept a list of std::string(), you can't call it with a char const *
value_in(std::string{"abc"}, "123");
because T, std::string, is different from Ts..., char const *, and SFINAE doesn't enable value_in.
You can use std::is_convertible instead of std::is_same but I suggest another way, in two steps.
First of all you need a custom type traits (with using helper) to select the first type from a list
template <typename T, typename ...>
struct firstType
{ using type = T; };
template <typename T, typename ... Ts>
using firstType_t = typename firstType<T, Ts...>::type;
Now you can write a first step value_in() that intercept all values, detect al types (without restriction) and pass they to a second step function as follows
template <typename T, typename ... Ts>
bool value_in (T val, Ts ... vals)
{ return value_in_helper<T, Ts...>(val, vals...); }
The second step function change the all Ts... type in T using firstType
template <typename T, typename ... Ts>
bool value_in_helper (T val, firstType_t<T, Ts> ... vals)
{
const std::unordered_set<T> allowed {val, vals ... };
return allowed.find(val) != allowed.end();
}
This solution is C++11 compatible.
Drawback: you need a second step.
Advantage (IMHO): this solution pass through a second step function that is declared receiving T types so accept also arguments that are convertible to T.
That is: this solution accept also
value_in(std::string{"abc"}, "123");
because there isn't anymore needs that "123" is exactly a std::string; can also be convertible to std::string.
I see two options here. You can pass a std::initializer_list, which causes the function signature to change to
#include <initializer_list>
template <typename T>
static bool value_in(T&& val, std::initializer_list<T> vals)
{
/* Implementation as before */
}
and the calling snippet to
return value_in(Enumeration::two,
{ Enumeration::one, Enumeration::two }) ? 0 : 1;
Note the additional braces here, they are required to construct the initializer list to be passed. A little detail of this approach is the function signature, which immediately reveals that there is only one type to deduce.
If it feels wrong to type the braces, stick with your original attempt and tweak your function such that
template <typename S, typename... T>
static bool value_in(S&& val, T&&... vals) {
const std::unordered_set<S> allowed {std::forward<T>(vals)...};
/* As before... */
}
This allows for calling the function as in your original snippet. In contrast to the above solution, this signature obviously has two template parameters, which might require a second look to see that it will fail if S differs from T.
This question already has an answer here:
partial type as template argument c++ [duplicate]
(1 answer)
Closed 6 years ago.
I'm trying to write an algorithm that should work with different containers (std::vector, QVector) containing the same type:
template<class Container>
boolean findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
This one gives me
'Container' is not a template
template<template<typename> class Container>
I get:
error: no matching function for call to 'findpeaks(cv::MatExpr, std::vector >&)'
...
note: template argument deduction/substitution failed:
error: wrong number of template arguments (2, should be 1)
Calling code:
cv::Mat m(data, true);
std::vector<std::pair<int, double>> peaks;
QVERIFY(daf::findpeaks(m.t(), peaks));
I've also tried something like this:
template<template< template<typename, typename> typename > class Container>
warning: ISO C++ forbids typename key in template template parameter; use -std=c++1z or -std=gnu++1z [-Wpedantic]
And some more errors...
std::vector has two template parameters.
template<
class T,
class Allocator = std::allocator<T>
> class vector;
And QVector has one. You can do it with variadic template:
template<template <typename...> class Container>
bool findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
Do you actually need Container to be a class template? Just make it a normal type:
template<class Container>
boolean findpeaks(cv::Mat &m, Container& peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
This will let you use other containers that are potentially not templates. Like, struct MySpecialPairContainer { ... };
You may do
template<template <typename ...> class Container>
bool findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
Your problem is that std::vector has 2 template parameters, the type T and an allocator.
But you can do even simpler:
template<typename Container>
bool findpeaks(cv::Mat& m, Container& peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
Just as expressed with #Barry's answer, I don't think you need a template template parameter here.
However, you seem to have concern about expressing "a container that supports push_back with a pair of..."
I suggest you to express this constraint in a sfinae constraint expression instead. Even if your parameter is explicitly requiring std::pair to be in the first template parameter of a class, it doesn't mean it has a push_back function, and doesn't mean the supposedly existing push_back is taking a std::pair as parameter.
Arguments of a function is currently a bad way of expressing what a template type should be able to do, or should be. You'll have to wait for concepts for that.
In the meantime, you can use a sfinae constraint in your function signature that explicitly express that you need a type that has a member push_back function that accept a std::pair:
template<class Container>
auto findpeaks(cv::Mat &m, Container& peaks)
// Using trailing return type
-> first_t<bool, decltype(peaks.push_back(std::make_pair(1, 1.0)))>
// Here's the constraint -^ that expression need to be valid
{
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
first_t can be implemented that way:
template<typename T, typename...>
using first_t = T;
For the function to exist, the expression inside the decltype must be valid. If the contraint is not satified, the compiler will try other overloads of the function findpeaks.
In general, you shouldn't over specify. If I wrote:
struct my_thing {
void push_back( std::pair<int, double> const& ) {}
};
shouldn't I be able to pass my_thing to your findpeaks?
There is absolutely no need for the template<class...>class Container template within the function, so requring it in the interface is an overspecification.
What you need is a sink (graph theoretical sink -- a sink is where things flow into, and don't flow out of) that consumes pairs of int, double. Ideally you want to be able to pass in a container without extra boilerplate.
template<class T>
struct sink:std::function<void(T)> {
using std::function<T>::function;
// more
};
now your function looks like:
bool findpeaks(cv::Mat &m, sink<std::pair<int, double>const&> peaks) {
// do stuff
peaks(std::make_pair(1, 1.0));
return true;
}
and as a bonus, you can now put it into a cpp file instead of a header. (The dispatching costs for a std::function are modest).
This does require that you wrap the second parameter up at the call site:
std::vector<std::pair<int, double>> v;
if(findpeaks( matrix, [&](auto&& e){v.push_back(decltype(e)(e));} ) {
// ...
which you might not like. Because we didn't use a naked std::function but instead a sink, we can get around this. First we write a metatrait, then some traits.
namespace details {
template<template<class...>class Z, class alwaysvoid, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
This is a meta trait that lets us write other traits.
template<class C, class X>
using push_back_result = decltype( std::declval<C>().push_back( std::declval<X>() ) );
template<class C, class X>
using can_push_back = can_apply< push_back_result, C, X >;
now we have a trait can_push_back that is true_type if and only if you can push an X into the container C.
We now augment sink:
template<class T, class Base=std::function<void(T)>>
struct sink:Base {
using Base::Base;
template<class C,
std::enable_if_t< can_push_back< C&, T >{}, int> =0
>
sink( C& c ):
Base( [&](auto&& t){ c.push_back(decltype(t)(t)); } )
{}
sink()=default;
sink(sink const&)=default;
sink(sink &&)=default;
sink& operator=(sink const&)=default;
sink& operator=(sink &&)=default;
};
This newly augmented sink now can be passed a container that supports .push_back(T) and automatically writes a function that solves the problem for you.
std::vector<std::pair<int, double>> v;
if(findpeaks( matrix, v ) {
// ...
That just works.
We can do the same for containers that support .insert(T), and after that we can use std::set<T> or even std::map<int, double> as a sink for your algorithm:
std::set<std::pair<int, double>> s;
if(findpeaks( matrix, s ) {
// ...
std::map<int, double> m;
if(findpeaks( matrix, m ) {
// ...
Finally, this also supports mocking. You can write a test-sink that helps unit-test your findpeaks algorithm directly.
I find the concept of sink used sufficiently often that having a sink type that supports these kind of things makes my code clearer, and reduces its dependency on any one kind of container.
Performance wise, the cost of the type erasure in std:function is modest. if you really need performance improvement, swapping sink<X> for sink<X, F> where F is a free parameter is possible, and writing make_sink that creates a sink where Base is a lambda, should work with near zero changes in the body of the code. But before you do that, you can work on higher level optimizations, like having the output into sink be processed in a streaming manner, or fed to an async queue, or the like.
I'm trying to implement a function similar to std::transform algorithm but instead of taking the output iterator by an argument I want to create and return a container with transformed input elements.
Let's say that it's named transform_container and takes two arguments: container and functor. It should return the same container type but possibly parametrized by a different element type (the Functor can return element of different type).
I'd like to use my function as in the example below:
std::vector<int> vi{ 1, 2, 3, 4, 5 };
auto vs = transform_container(vi, [] (int i) { return std::to_string(i); });
//vs will be std::vector<std::string>
assert(vs == std::vector<std::string>({"1", "2", "3", "4", "5"}));
std::set<int> si{ 5, 10, 15 };
auto sd = transform_container(si, [] (int i) { return i / 2.; });
//sd will be of type std::set<double>
assert(sd == std::set<double>({5/2., 10/2., 15/2.}));
I was able two write two functions — one for std::set and one for std::vector — that seem to work properly. They are identical, except of the container typename. Their code is listed below.
template<typename T, typename Functor>
auto transform_container(const std::vector<T> &v, Functor &&f) -> std::vector<decltype(f(*v.begin()))>
{
std::vector<decltype(f(*v.begin()))> ret;
std::transform(std::begin(v), std::end(v), std::inserter(ret, ret.end()), f);
return ret;
}
template<typename T, typename Functor>
auto transform_container(const std::set<T> &v, Functor &&f) -> std::set<decltype(f(*v.begin()))>
{
std::set<decltype(f(*v.begin()))> ret;
std::transform(std::begin(v), std::end(v), std::inserter(ret, ret.end()), f);
return ret;
}
However, when I attempted to merge them into a single general function that works with any container, I encountered numerous issues. The set and vector are class templates, so my function template must take a template template parameter. Moreover, set and vector templates have a different number of type parameters that needs to be properly adjusted.
What is the best way to generalize the two function templates above into a function that works with any compatible container type?
Simplest cases: matching container types
For the simple case where the input type matches the output type (which I've since realized is not what you're asking about) go one level higher. Instead of specifying the type T that your container uses, and trying to specialize on a vector<T>, etc., just specify the type of the container itself:
template <typename Container, typename Functor>
Container transform_container(const Container& c, Functor &&f)
{
Container ret;
std::transform(std::begin(c), std::end(c), std::inserter(ret, std::end(ret)), f);
return ret;
}
More complexity: compatible value types
Since you want to try to change the item type stored by the container, you'll need to use a template template parameter, and modify the T to that which the returned container uses.
template <
template <typename T, typename... Ts> class Container,
typename Functor,
typename T, // <-- This is the one we'll override in the return container
typename U = std::result_of<Functor(T)>::type,
typename... Ts
>
Container<U, Ts...> transform_container(const Container<T, Ts...>& c, Functor &&f)
{
Container<U, Ts...> ret;
std::transform(std::begin(c), std::end(c), std::inserter(ret, std::end(ret)), f);
return ret;
}
What of incompatible value types?
This only gets us partway there. It works fine with a transform from signed to unsigned but, when resolving with T=int and U=std::string, and handling sets, it tries to instantiate std::set<std::string, std::less<int>, ...> and thus doesn't compile.
To fix this, we want to take an arbitrary set of parameters and replace instances of T with U, even if they are the parameters to other template parameters. Thus std::set<int, std::less<int>> should become std::set<std::string, std::less<std::string>>, and so forth. This involves some custom template meta programming, as suggested by other answers.
Template metaprogramming to the rescue
Let's create a template, name it replace_type, and have it convert T to U, and K<T> to K<U>. First let's handle the general case. If it's not a templated type, and it doesn't match T, its type shall remain K:
template <typename K, typename ...>
struct replace_type { using type = K; };
Then a specialization. If it's not a templated type, and it does match T, its type shall become U:
template <typename T, typename U>
struct replace_type<T, T, U> { using type = U; };
And finally a recursive step to handle parameters to templated types. For each type in a templated type's parameters, replace the types accordingly:
template <template <typename... Ks> class K, typename T, typename U, typename... Ks>
struct replace_type<K<Ks...>, T, U>
{
using type = K<typename replace_type<Ks, T, U>::type ...>;
};
And finally update transform_container to use replace_type:
template <
template <typename T, typename... Ts> class Container,
typename Functor,
typename T,
typename U = typename std::result_of<Functor(T)>::type,
typename... Ts,
typename Result = typename replace_type<Container<T, Ts...>, T, U>::type
>
Result transform_container(const Container<T, Ts...>& c, Functor &&f)
{
Result ret;
std::transform(std::begin(c), std::end(c), std::inserter(ret, std::end(ret)), f);
return ret;
}
Is this complete?
The problem with this approach is it is not necessarily safe. If you're converting from Container<MyCustomType> to Container<SomethingElse>, it's likely fine. But when converting from Container<builtin_type> to Container<SomethingElse> it's plausible that another template parameter shouldn't be converted from builtin_type to SomethingElse. Furthermore, alternate containers like std::map or std::array bring more problems to the party.
Handling std::map and std::unordered_map isn't too bad. The primary problem is that replace_type needs to replace more types. Not only is there a T -> U replacement, but also a std::pair<T, T2> -> std::pair<U, U2> replacement. This increases the level of concern for unwanted type replacements as there's more than a single type in flight. That said, here's what I found to work; note that in testing I needed to specify the return type of the lambda function that transformed my map's pairs:
// map-like classes are harder. You have to replace both the key and the key-value pair types
// Give a base case replacing a pair type to resolve ambiguities introduced below
template <typename T1, typename T2, typename U1, typename U2>
struct replace_type<std::pair<T1, T2>, std::pair<T1, T2>, std::pair<U1, U2>>
{
using type = std::pair<U1, U2>;
};
// Now the extended case that replaces T1->U1 and pair<T1,T2> -> pair<T2,U2>
template <template <typename...> class K, typename T1, typename T2, typename U1, typename U2, typename... Ks>
struct replace_type<K<T1, T2, Ks...>, std::pair<const T1, T2>, std::pair<const U1, U2>>
{
using type = K<U1, U2,
typename replace_type<
typename replace_type<Ks, T1, U1>::type,
std::pair<const T1, T2>,
std::pair<const U1, U2>
>::type ...
>;
};
What about std::array?
Handling std::array adds to the pain, as its template parameters cannot be deduced in the template above. As Jarod42 notes, this is due to its parameters including values instead of just types. I've gotten partway by adding specializations and introducing a helper contained_type that extracts T for me (side note, per Constructor this is better written as the much simpler typename Container::value_type and works for all types I've discussed here). Even without the std::array specializations this allows me to simplify my transform_container template to the following (this may be a win even without support for std::array):
template <typename T, size_t N, typename U>
struct replace_type<std::array<T, N>, T, U> { using type = std::array<U, N>; };
// contained_type<C>::type is T when C is vector<T, ...>, set<T, ...>, or std::array<T, N>.
// This is better written as typename C::value_type, but may be necessary for bad containers
template <typename T, typename...>
struct contained_type { };
template <template <typename ... Cs> class C, typename T, typename... Ts>
struct contained_type<C<T, Ts...>> { using type = T; };
template <typename T, size_t N>
struct contained_type<std::array<T, N>> { using type = T; };
template <
typename Container,
typename Functor,
typename T = typename contained_type<Container>::type,
typename U = typename std::result_of<Functor(T)>::type,
typename Result = typename replace_type<Container, T, U>::type
>
Result transform_container(const Container& c, Functor &&f)
{
// as above
}
However the current implementation of transform_container uses std::inserter which does not work with std::array. While it's possible to make more specializations, I'm going to leave this as a template soup exercise for an interested reader. I would personally choose to live without support for std::array in most cases.
View the cumulative live example
Full disclosure: while this approach was influenced by Ali's quoting of Kerrek SB's answer, I didn't manage to get that to work in Visual Studio 2013, so I built the above alternative myself. Many thanks to parts of Kerrek SB's original answer are still necessary, as well as to prodding and encouragement from Constructor and Jarod42.
Some remarks
The following method allows to transform containers of any type from the standard library (there is a problem with std::array, see below). The only requirement for the container is that it should use default std::allocator classes, std::less, std::equal_to and std::hash function objects. So we have 3 groups of containers from the standard library:
Containers with one non-default template type parameter (type of value):
std::vector, std::deque, std::list, std::forward_list, [std::valarray]
std::queue, std::priority_queue, std::stack
std::set, std::unordered_set
Containers with two non-default template type parameters (type of key and type of value):
std::map, std::multi_map, std::unordered_map, std::unordered_multimap
Container with two non-default parameters: type parameter (type of value) and non-type parameter (size):
std::array
Implementation
convert_container helper class convert types of known input container type (InputContainer) and output value type (OutputType) to the type of the output container(typename convert_container<InputContainer, Output>::type):
template <class InputContainer, class OutputType>
struct convert_container;
// conversion for the first group of standard containers
template <template <class...> class C, class IT, class OT>
struct convert_container<C<IT>, OT>
{
using type = C<OT>;
};
// conversion for the second group of standard containers
template <template <class...> class C, class IK, class IT, class OK, class OT>
struct convert_container<C<IK, IT>, std::pair<OK, OT>>
{
using type = C<OK, OT>;
};
// conversion for the third group of standard containers
template
<
template <class, std::size_t> class C, std::size_t N, class IT, class OT
>
struct convert_container<C<IT, N>, OT>
{
using type = C<OT, N>;
};
template <typename C, typename T>
using convert_container_t = typename convert_container<C, T>::type;
transform_container function implementation:
template
<
class InputContainer,
class Functor,
class InputType = typename InputContainer::value_type,
class OutputType = typename std::result_of<Functor(InputType)>::type,
class OutputContainer = convert_container_t<InputContainer, OutputType>
>
OutputContainer transform_container(const InputContainer& ic, Functor f)
{
OutputContainer oc;
std::transform(std::begin(ic), std::end(ic), std::inserter(oc, oc.end()), f);
return oc;
}
Example of use
See live example with the following conversions:
std::vector<int> -> std::vector<std::string>,
std::set<int> -> std::set<double>,
std::map<int, char> -> std::map<char, int>.
Problems
std::array<int, 3> -> std::array<double, 3> conversion doesn't compile because std::array haven't insert method which is needed due to std::inserter). transform_container function shouldn't also work for this reason with the following containers: std::forward_list, std::queue, std::priority_queue, std::stack, [std::valarray].
Doing this in general is going to be pretty hard.
First, consider std::vector<T, Allocator=std::allocator<T>>, and let's say your functor transforms T->U. Not only do we have to map the first type argument, but really we ought to use Allocator<T>::rebind<U> to get the second. This means we need to know the second argument is an allocator in the first place ... or we need some machinery to check it has a rebind member template and use it.
Next, consider std::array<T, N>. Here we need to know the second argument should be copied literally to our std::array<U, N>. Perhaps we can take non-type parameters without change, rebind type parameters which have a rebind member template, and replace literal T with U?
Now, std::map<Key, T, Compare=std::less<Key>, Allocator=std::allocator<std::pair<Key,T>>>. We should take Key without change, replace T with U, take Compare without change and rebind Allocator to std::allocator<std::pair<Key, U>>. That's a little more complicated.
So ... can you live without any of that flexibility? Are you happy to ignore associative containers and assume the default allocator is ok for your transformed output container?
The major difficulty is to somehow get the container type Container from Conainer<T>. I have shamelessly stolen the code from template metaprogramming: (trait for?) dissecting a specified template into types T<T2,T3 N,T4, ...>, in particular, Kerrek SB's answer (the accepted answer), as I am not familiar with template metaprogramming.
#include <algorithm>
#include <cassert>
#include <type_traits>
// stolen from Kerrek SB's answer
template <typename T, typename ...>
struct tmpl_rebind {
typedef T type;
};
template <template <typename ...> class Tmpl, typename ...T, typename ...Args>
struct tmpl_rebind<Tmpl<T...>, Args...> {
typedef Tmpl<Args...> type;
};
// end of stolen code
template <typename Container,
typename Func,
typename TargetType = typename std::result_of<Func(typename Container::value_type)>::type,
typename NewContainer = typename tmpl_rebind<Container, TargetType>::type >
NewContainer convert(const Container& c, Func f) {
NewContainer nc;
std::transform(std::begin(c), std::end(c), std::inserter(nc, std::end(nc)), f);
return nc;
}
int main() {
std::vector<int> vi{ 1, 2, 3, 4, 5 };
auto vs = convert(vi, [] (int i) { return std::to_string(i); });
assert( vs == std::vector<std::string>( {"1", "2", "3", "4", "5"} ) );
return 0;
}
I have tested this code with gcc 4.7.2 and clang 3.5 and works as expected.
As Yakk points out, there are quite a few caveats with this code though: "... should your rebind replace all arguments, or just the first one? Uncertain. Should it recursively replace T0 with T1 in later arguments? Ie std::map<T0, std::less<T0>> -> std::map<T1, std::less<T1>>?" I also see traps with the above code (e.g. how to deal with different allocators, see also Useless' answer).
Nevertheless, I believe the above code is already useful for simple use cases. If we were writing a utility function to be submitted to boost, then I would be more motivated to investigate these issues further. But there is already an accepted answer so I consider the case closed.
Many thanks to Constructor, dyp and Yakk for pointing out my mistakes / missed opportunities for improvements.
I wrote a blog post to solve a similar problem recently. Using templates and the iterator interface was the route I chose to follow.
for_each:
To cut down on the amount of boilerplate, we're going to create a using clause that allows us to grab the type contained within an iterator:
template <typename IteratorType>
using ItemType = typename std::iterator_traits<typename IteratorType::iterator>::value_type;
With that in place, we can implement a helper function for_each like so:
template <typename IteratorType>
void for_each(IteratorType &items, std::function<void(ItemType<IteratorType> const &item)> forEachCb)
{
for (typename IteratorType::iterator ptr = items.begin(); ptr != items.end(); ++ptr)
forEachCb(*ptr);
}
transform_container:
Finally transform_container, could be implemented like so:
template <typename IteratorType, typename ReturnType>
ReturnType transform_container(IteratorType &items, std::function<ItemType<ReturnType>(ItemType<IteratorType> const &item)> mapCb)
{
ReturnType mappedIterator;
for_each<IteratorType>(items, [&mappedIterator, &mapCb](auto &item) { mappedIterator.insert(mappedIterator.end(), mapCb(item)); });
return mappedIterator;
}
Which will allow us to call your two examples in the following way:
std::vector<int> vi{ 1, 2, 3, 4, 5 };
auto vs = transform_container<std::vector<int>, std::vector<std::string>>(vi, [](int i){return std::to_string(i);});
assert(vs == std::vector<std::string>({"1", "2", "3", "4", "5"}));
std::set<int> si{ 5, 10, 15 };
auto sd = transform_container<std::set<int>, std::set<double>>(si, [] (int i) { return i / 2.; });
assert(sd == std::set<double>({5/2., 10/2., 15/2.}));
My blog post also goes into a little more detail if that's helpful.
This code does not automatically infer the return type correctly (a design aspect of C++):
template < typename Container,
typename UnaryOp>
Container
mymap(Container c, UnaryOp op)
{
typedef typename Container::value_type ResultType
Container<ResultType> result;
for(Container::iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
What I would like to do is have something like this happen:
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic;
foomatic = mymap(bar, [] (string s)->int {return atoi(s.c_str());});
//foomatic now is equal to {1,2,3}
I was figuring that Container would be inferred to be vector, and ResultType would be inferred to be int.
After the question changed:
You are using the same type, Container, for input and output. But your input and output types are distinct: your input is vector<string>, whereas your output is vector<int>. No wonder that C++ refuses to compile this.
Your problem is now to deduce the return type from the input types. Generally, C++ cannot do that. It’s as simple as that: overload resolution and template resolution only happens based on the input arguments, never on the return type (in some cases elaborate tricks involving proxy objects and implicit casts can be used to work around this but let’s not go there).
The simplest and most idiomatic solution is just to specify the return element type manually when calling the function, as in:
foomatic = mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
This requires that the return element type be put first in the template argument list:
template <
typename ResultType,
template<typename> class Container,
typename InputType,
typename UnaryOp>
Container<ResultType> mymap(Container<InputType> c, UnaryOp op) { ... }
However, that does not work because std::vector does not fit the declaration of template<typename> class. Why? Simple reason: because it has more than just one template argument. In particular, the standard says that it has at least one extra template argument to specify the allocator.
Solution: declare the template argument as template<typename, typename> class, right?
No. Now, this does work for some standard library implementations. But besides the mandated two template arguments, the containers may have additional template arguments that take default values (this is often used to pass policy classes to a container, for example; the allocator is already such a policy class).
This is a fundamental problem: we cannot declare Container so that it conforms to all possible type signatures of containers in C++. So this solution, too, is a no-go.
The best solution is unfortunately more complicated, we need to rebind the container type explicitly. This we can do via an extra metafunction:
template <typename C, typename T>
struct rebind;
We need to partially specialize this metafunction for each possible number of template parameters. For example, to make it work with the minimal std::vector, we’d need the following partial specialization:
template <
template <typename, typename> class C,
typename Old,
typename New,
typename A>
struct rebind<C<Old, A>, New> {
typedef typename A::template rebind<New> Rebound;
typedef C<New, typename Rebound::other> type;
};
This looks daunting. What it does is take an existing std::vector<foo> and a type bar and rewrite it to a std::vector<bar>. The tricky part is that we also need to rewrite the allocator type. This is done via the rather complicated Rebound declaration.
Now we can write your function, and invoke it:
template <
typename ResultType,
typename C,
typename UnaryOp>
typename rebind<C, ResultType>::type
mymap(C const& c, UnaryOp op)
{
typename rebind<C, ResultType>::type result;
for(typename C::const_iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
int main() {
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic =
mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
}
Piece of cake. A really, really, complicated cake.
Answer to old question:
If you have a template parameter that is itself a class template, you need to declare it as such:
template <
template<typename> class Container,
typename ResultType,
typename UnaryOp>
Container<ResultType> mymap(Container<ResultType> c, UnaryOp op) { ... }
The template<typename> class Container mimics the class template declaration syntax and tells the compiler that “Container is a class template that expects a single template argument.”
But libraries usually avoid these nested template declarations and instead rely on traits/metafunctions to communicate such information. That is, it would usually be written as follows:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Container::value_type ResultType;
}
(The typename in the typedef is necessary because the name is a dependent name and C++ cannot figure out that this it names a type.)
This example mimics the standard library convention of having a typedef value_type inside each container for its associated value type. Other libraries may follow different schemas. For example, I am contributing to a library that uses external metafunctions that work as follows:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Value<Container>::Type ResultType;
}
The idea is the same, the only difference is that Container::value_type has been “outsourced” to an independent type.
You need something along the lines of:
template<typename Container, typename UnaryOp>
auto mymap(Container c, UnaryOp op) -> Container::rebind<decltype(op(*c.begin()))>
{
typedef typename Container::value_type InputType;
typedef decltype( op( InputType() ) ) ResultType;
typedef typename Container::rebind<ResultType> ResultContainer;
// ...
}
You can use a trick known as auto_cast, which we will rewrite a little bit to be specific to containers.
template<typename container> struct auto_cast_container {
container c;
template<typename out_type> operator out_type() {
return out_type(c.begin(), c.end());
}
};
template<typename Container, typename UnaryOperator>
auto
mymap(const Container& c, UnaryOperator op)
-> auto_cast_container<std::vector<decltype(op(*c.begin()))>> {
std::vector<decltype(op(*c.begin()))> retval;
std::for_each(c.begin(), c.end(), [&](decltype(*c.begin())& ref) {
retval.push_back(op(ref));
});
auto_cast_container<std::vector<decltype(op(*c.begin()))>> return_value;
return_value.c = std::move(retval);
return return_value;
}
Effectively, the templated conversion operator allows for conversion to any type which will accept the begin/end constructor. This means that you can map from a vector to a list, if you like, and it can also map pairs into associative containers and back again, should you need it. If you're hunting for efficiency this can be tuned further but I left that out for clarity.
Edit: Konrad's comment pointed out a couple of logical flaws. I also improved the safety and transparency of the system by using decltype in all appropriate cases.