I'm trying to use templates with multiple to pass data onto a function, but using only the first template argument as a filter. Something like this:
template <typename A, typename B>
class Component {
};
template <typename A>
class Context {
public:
void add(Component<A, void *> comp) {
}
}
typedef struct Foo { int x; } Foo;
typedef struct Bar { int y; } Bar;
Context<Foo> *context = new Context<Foo>();
Component<Foo, Bar> *comp = new Component<Foo, Bar>();
context->add(comp); // error
But the compiler complains that it cannot convert Component<Foo, Bar> to Component<Foo, void *>. Is there a way to accomplish this?
I think what you probably need to do is change the signature of the 'add' method:
template <typename A>
class Context
{
public:
template<class B>
void add(Component<A, B> comp)
{
}
};
However, I don't know the details of your problem so this is a mere guess.
I'm trying to use templates with multiple to pass data onto a function, but using only the first template argument as a filter. [...] But the compiler complains that it cannot convert Component to Component. Is there a way to accomplish this?
Well, your filter works doesn't it: your add function will only match a Component whose second template parameter is void*, and you're providing Bar. What else could you possibly expect? If you want it to handle other "second-parameters" as well, either remove the filter, provide an fallback function for it to match, or some kind of conversion.
Yes, add a converting copy constructor to your Component:
template<class U, class V>
Component(Component<U,V> const& other){
// ...
};
But that is still refineable with the appropriate enable_if SFINAE guard:
// <tr1/type_traits> for C++03
#include <type_traits> // for C++0x
template<class T, class U>
struct can_convert{
// std::tr1::... for C++03
static bool const value =
std::is_same<T,U>::value || std::is_convertible<T,U>::value;
};
template<class C1, class C2>
struct ice_and{
static bool const value = C1::value && C2::value;
}
// define for clarity and brevity
#define IF_CAN_CONVERT(A,B,U,V) \
typename std::enable_if<ice_and<can_convert<A,U>,can_convert<B,V> > >::type* = 0
template<class U, class V>
Component(Component<U,V> const& other, IF_CAN_CONVERT(A,B,U,V)){
// ...
};
Related
I'm learning templates. If I mix up the concepts template / template-type / template-argument, please correct me.
I'm trying to write a template function that creates an object and returns it. The type of the object comes from the template argument that has to be explicitly specified.
result = createObject<ObjectType>();
This object though is supposed to be a template. A container for example. And the function is supposed to know the type of the object and its template arguments. Ex:
result = createObject<Container<ElementType>>();
I've tried to solve it with template template parameter:
template <template<class> class ContainerType, class ElementType>
auto createObject()
{
ContainerType<ElementType> result;
//do stuff...
return result;
}
//...
template<typename T>
struct Vector{};
//...
//const auto random_vec = createObject<Vector<float>>(); // ERROR.
const auto random_vec = createObject<Vector, float>();
The second case works, the first doesn't. It says candidate template ignored: invalid explicitly-specified argument for template parameter 'ContainerType'.
Is it possible to make it work like the first case? Give it something like Vector<float> and it can deduce the ContainerType to Vector and ElementType to float? Is it possible to overload or specialize this function so that it handles certain types of containers differently? Should I use concepts?
The usual way to do decomposition like this is via partial specialization, which requires a helper class template:
namespace detail {
template<class> struct create; // undefined
template<template<class T> class C,class T>
struct create<C<T>> {
static C<T> make() {/* … */}
};
}
template<class T>
T createObject() {return detail::create<T>::make();}
The primary template can be defined if you want to support the general case, and other specializations may be added for other kinds of templates like std::array.
You could create a type trait to check if the type is instantiated from a template:
#include <type_traits>
// trait to check if the type is instantiated from a template
template<typename T>
struct is_template_instance_type : std::false_type {};
template<template<class,class...> class C, class T, class... Rest>
struct is_template_instance_type<C<T,Rest...>> : std::true_type {
using class_type = C<T,Rest...>;
using value_type = T;
// using rest_types = std::tuple<Rest...>;
};
// Helper variable template - if needed for something later
template<class T>
inline constexpr bool is_template_instance_type_v = is_template_instance_type<T>::value;
You could then add overloads:
template<class T, class C = is_template_instance_type<T>, class U = typename C::class_type>
auto createObject() {
U result;
// typename C::value_type x; // if you need the value type
return result;
}
template<template<class,class...> class C, class T, class... Rest>
auto createObject() {
return createObject< C<T,Rest...> >();
}
And it would then work with Vector<float>, Vector, float but not float for example.
Demo
You can simply go like this:
template<typename T, typename V = typename T::value_type>
T createObject()
{
T t {}; // T will be e.g std::vector<int>
V v {}; // V will be int
// do work...
t.push_back(v++);
t.push_back(v++);
// ...work done
return t;
}
Than you can use it like this:
int main ()
{
auto obj1 = createObject<std::vector<int>>();
auto obj2 = createObject<std::list<double>>();
return 0;
}
Given I have a template setup to do something on a type such as...
template<typename T>
class SimpleTemplate
{
private:
T m_obj;
public:
void operator()() { m_obj.DoSomething(); }
};
And I want to handle the case where I have a collection of type T the same way. I currently have a template setup like so for a vector...
template<typename T>
class SimpleTemplate<std::vector<T>>
{
private:
std::vector<T> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
Now I want to also support sets, unordered_sets and so on. I could write a template for each collection but I feel like this should be a perfect job for a template, only I can't figure out how it should be written, or even if it can be?
Can I do something like template<typename C<T>>?
As mentioned by Geoffroy, you can use a trait to detect whether T can be iterated over. You then use this to select the correct specialization.
So start off with the "is_iterable" trait shown by Jarod42 here:
// Code by Jarod42 (https://stackoverflow.com/a/29634934).
#include <iterator>
#include <type_traits>
namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;
template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});
template <typename T>
std::false_type is_iterable_impl(...);
}
template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));
This gives you an is_iterable<T> trait which inherits from either std::true_type or std::false_type. Now use this with SFINAE to create two specializations:
template <class T, bool = is_iterable<T>::value>
class SimpleTemplate;
template <class T>
class SimpleTemplate<T, false> {
private:
T m_obj;
public:
SimpleTemplate (T obj) : m_obj(std::move(obj)) { }
void operator() () { m_obj.DoSomething(); }
};
template <class T>
class SimpleTemplate<T, true> {
private:
T m_collection;
public:
SimpleTemplate (T obj) : m_collection(std::move(obj)) { }
void operator() () {
for (auto && obj : m_collection) { obj.DoSomething(); }
}
};
Since both partial specializations are mutually exclusive for any given T, you won't get any errors about ambiguity.
Edit: Changed 2nd template argument into a bool instead of class. This makes it simple to fully specialize it in case the default behavior is unwanted.
E.g. for std::string, which for which is_iterable is true, simply do the following. Note that I added constructors to SimpleTemplate, I couldn't get the full specialization to inherit the base class' constructor otherwise.
template <>
class SimpleTemplate<std::string, true>
: public SimpleTemplate<std::string, false> {
// Inherit constructor.
using base = SimpleTemplate<std::string, false>;
using base::base;
};
Now I want to also support sets, unordered_sets and so on. I could write a template for each collection but I feel like this should be a perfect job for a template, only I can't figure out how it should be written, or even if it can be
Maybe you can use a template-template parameter
template <template <typename...> class C, typename... Ts>
class SimpleTemplate<C<Ts...>>
{
private:
C<Ts...> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
This should intercept std::(unordered_)(multi)set, std::vector, std::deque, etc.
Unfortunately doesn't intercept std::array, because it's second template parameter is a value, not a type.
So i've been coding my own unique_ptr class and i have to handle arrays in a different way that i handle other types.
template <typename TYPE, bool _arr = std::is_array<TYPE>::value>
class scoped_ptr final {
private:
typedef std::remove_extent_t<TYPE> gen;
gen* m_data;
public:
//some methods
void reset();
};
template<typename TYPE ,bool _arr>
inline void scoped_ptr<TYPE, _arr>::reset()
{
//some code...
}
The problem is that i want the reset method to only be avaiable to non array allocations, when using std::enable_if i get the error: "A default template argument cannot be specified on the declaration of a member of a class template outside of its class" despite the code still compiling
template<typename TYPE ,bool _arr>
class scoped_ptr final {
public:
template<typename = typename std::enable_if<!_arr>::type>
void reset();
};
template<typename TYPE ,bool _arr>
template<typename = typename std::enable_if<!_arr>::type>
inline void scoped_ptr<TYPE, _arr>::reset()
{
}
I also tryed this, but it also gives an error: "template argument list must match parameter list"
template<typename TYPE ,bool _arr>
inline void scoped_ptr<TYPE, false>::reset()
{
}
Does anybody have an idea on how can i disable this method for arrays? I know i could specialize the class scoped_ptr but i wanted to avoid code duplication. Is there any way to do it?
Edit:
After reading the responses i changed the code to this:
template <typename TYPE, bool is_arr = std::is_array<TYPE>::value>
class scoped_ptr final {
private:
typedef std::remove_extent_t<TYPE> gen;
gen* m_data;
public:
//Some methods
template<bool _arr = is_arr, typename = typename std::enable_if<!_arr>::type>
void reset();
};
template<typename TYPE, bool is_arr>
template<bool, typename>
inline void scoped_ptr<TYPE, is_arr>::reset()
{
}
The code compiles with no errors, until i try to call the method:
int main() {
scoped_ptr<int> ptr = new int;
ptr.reset();
}
Thats when i get the error: "void scoped_ptr«int,false»::reset(void): could not deduce template argument for «unnamed-symbol»"
But if i write the implementation inside of the class the error goes away. How can i fix this?
If you want to make reset() SFINAE-friendly, make it a fake template:
template<bool is_arr = _arr, typename = std::enable_if_t<is_arr>>
void reset();
Note that SFINAE works when a template is instantiated, and the condition should depend on the template parameter. This is not a valid SFINAE construct:
template<typename = typename std::enable_if<!_arr>::type>
void reset();
If you don't care about SFINAE-friendliness, use static_assert() inside reset().
Edit.
Consider the following simple code as a demonstration of valid and invalid SFINAE:
template<class T, bool f = std::is_integral_v<T>>
struct S {
// template<typename = std::enable_if_t<f>> // (invalid)
template<bool _f = f, typename = std::enable_if_t<_f>> // (valid)
void reset() {}
};
template<class T, typename = void>
struct has_reset : std::false_type {};
template<class T>
struct has_reset<T, std::void_t<decltype(std::declval<T>().reset())>> : std::true_type {};
void foo() {
has_reset<S<int>>::value;
has_reset<S<void>>::value;
}
It will fail to compile if your replace the (valid) line with the (invalid) one.
Edit 2.
When you define a member function outside the class, you don't repeat default values of template parameters:
template<class T, bool f>
template<bool, typename>
void S<T, f>::reset() { }
Edit 3.
For some reason (a compiler bug I suppose) MSVC rejects this definition with the error: "Could not deduce template argument for «unnamed-symbol»". It can be fixed by adding a name for the bool parameter:
template<class T, bool f>
template<bool _f, typename>
void S<T, f>::reset() { }
This name should match that in the declaration.
Let's say I have a template function:
template <typename A, typename B>
A fancy_cast(B)
{
return {};
}
The intended usage is something like fancy_cast<int>(1.f).
But nothing stops the user from specifying the second template parameter manually: fancy_cast<int, int>(1.f), which would cause problems.
How can I prevent typename B from being specified and force it to be deduced?
I've come up with this:
// Using this wrapper prevents the code from being
// ill-formed NDR due to [temp.res]/8.3
template <auto V> inline constexpr auto constant_value = V;
template <
typename A,
typename ...Dummy,
typename B,
typename = std::enable_if_t<constant_value<sizeof...(Dummy)> == 0>
>
A fancy_cast(B)
{
return {};
}
It appears to work, but it's extremely cumbersome. Is there a better way?
What about making fancy_cast a variable template?
template <typename A>
struct fancy_cast_t {
template <typename B>
A operator()(B x) const { return x; }
};
template <typename A>
constexpr fancy_cast_t<A> fancy_cast {};
fancy_cast<int>(1.5); // works
fancy_cast<int, int>(1.5); // doesn't work
fancy_cast<int>.operator()<int>(1.5); // works, but no one would do this
This is not the most efficient solution, but you can create a class that has a template parameter for the type to convert to, and then have a constructor template that takes any type. Then if you add an operator T for the type you instantiate the class with you can have that return correct value. That would look like
template<typename T>
struct fancy_cast
{
T ret;
template<typename U>
fancy_cast(U u) : ret(u) {} // or whatever you want to do to convert U to T
operator T() && { return std::move(ret); }
};
int main()
{
double a = 0;
int b = fancy_cast<int>(a);
}
This works because there is no way to specify the template parameter for the constructor since you can't actually call it.
I found a good-looking solution.
We can use a non-type parameter pack, of a type that the user can't construct.1 E.g. a reference to a hidden class:
namespace impl
{
class require_deduction_helper
{
protected:
constexpr require_deduction_helper() {}
};
}
using require_deduction = impl::require_deduction_helper &;
template <typename A, require_deduction..., typename B>
A fancy_cast(B)
{
return {};
}
1 We do have to leave a loophole for constructing a deduction_barrier, otherwise the code would be ill-formed NDR. That's why the constructor is protected.
I have a pair of class template declarations like this:
template<typename T, template<typename W> class X>
struct B {
bool operator()(const T& left, const T& right)
{ return X<W>()(left, right); }
};
template<template<typename T> class X, typename Q>
struct A {
bool operator()(const int a, const int b)
{
return B<Q, X>()(a, b);
}
};
I can create A<std::greater, int>, A<std::less, int> and it works as I want.
Is it possible to create a class that will contain a template passed in like std::greater and still be able to be passed like std::greater itself in the example above?
Something like this:
template <template <typename T> class Class>
class Wrapper
{
operator()(const int, const int) { return Class<?what's here?> (value, value); }
};
I would like to pass my Wrapper to an unchanged A
A<Wrapper<std::less>, long>
In other words, I want to imitate a template passed to my wrapper, to be compatible with actual version of class A.
The short answer is no, not quite like you want it to be. The first argument of A must be a template name. But when you add arguments and write Wrapper<std::less>, you get a class type. It is no longer a template. Think of it this way, A expects a cookie cutter, but you pass a cookie made from the cutter instead.
That's not to say you can't turn a template into another template, but it won't be quite so opaque. The solution is to add a member template to Wrapper. Then Wrapper<std::less>, despite being a class type, will have a member that is a template. And you can pass that in to A.
template <template <typename T> class Class>
struct Wrapper
{
template<typename U> // member template
struct temp {
bool operator()(const int v1, const int v2)
{ return Class<U>()(v1, v2); }
};
};
This enables you to write A<Wrapper<std::less>::temp, long>.
Check it out live.