In my c++ template struct I want to use different container types which use different allocators, e.g. std::vector and thrust::device_vector.
I need to specify the allocator explicitely, otherwise I get "wrong number of template arguments (1, should be 2)":
template<typename T, template <typename, typename> class Container, typename Alloc>
struct wrap_into_container
{
typedef Container<T, Alloc> type;
};
Since the different container classes use different allocators, I have to specify the corresponding allocator everytime I want to use this template.
How can I get the allocator depending on the Container type without having to specify it?
I thought of using a traits struct which I then specialize for each Container type, but I don't know how to implement it or if it is even useful / possible / ...
UPDATE:
I cannot use C++11 unfortunately due to restrictions of the NVIDIA compiler ...
In c++11, I favour variadics
template<typename T, template <typename...> class Container>
struct wrap_into_container
{
typedef Container<T>::type type;
};
(I haven't checked whether C::type is actually a wellformed expression for standard container types)
To the comment:
template<typename T, template <typename...> class Container>
struct wrap_into_container
{
typedef Container<T>::type type;
};
For C++03 you can emulate a template alias using nested typedefs, essentially making a unary type-function taking a single element type and returning a container of that type. The concept:
#include <vector>
#include <deque>
#include <set>
#include <list>
namespace container
{
template <typename T> struct vector { typedef std::vector<T> type; };
template <typename T> struct set { typedef std::set <T> type; };
template <typename T> struct list { typedef std::list <T> type; };
template <typename T> struct deque { typedef std::deque <T> type; };
}
template<typename T, template <typename> class Container>
struct wrap_into_container
{
typedef typename Container<T>::type type;
};
#include <string>
int main() {
wrap_into_container<int, container::set>::type ws;
wrap_into_container<double, container::list>::type wl;
wrap_into_container<bool, container::deque>::type wd;
wrap_into_container<std::string, container::vector>::type wv;
return ws.size() + wl.size() + wd.size() + wv.size();
}
See it Live On Coliru
Related
Say, I have some template which specialized for several types, TypeMathcer, which has type member.
#include <memory>
#include <vector>
template <typename T>
struct TypeMatcher;
template <typename T>
struct TypeMatcher<T *>
{
// making some type from T
typedef std::shared_ptr<T> type;
};
template <typename T>
struct TypeMatcher<T&>
{
// making other type from T
typedef std::vector<T> type;
};
Now, I want to create another template and specialize it for types I get from TypeMatcher. If I do it straightforward, like this
template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
{
};
I get compiler error: template parameters not deducible in partial specialization.
Same error if use using syntax
template <typename T>
using type_matcher_t = typename TypeMatcher<T>::type;
template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<type_matcher_t<T> >
{
};
I read answer to question partial specialization for iterator type of a specified container type that is very similar to my question, but still not sure if existing of one counter-example makes all question senseless. Also now we have brand-new c++14 and c++17 standards which could change situation. So what if I ensure the specializations is unique and exists, will than any possibility to make parameters deducible?
This is impossible, on principle, and no fancy C++9999 can change that.
What you're asking the compiler to do:
There's a use such as MyNeedfulTemplate<int> in the code. The compiler needs a definition of MyNeedfulTemplate<U> for U = int. You've tried to provide a partial specialisation of the form
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
To see whether this specialisation applies or not, the compiler would have to inspect TypeMatcher<T> for all possible Ts and find if any one of them has a nested typedef type that aliases int. This cannot happen, as the set of "all possible Ts" is infinite. OK, TypeMatcher<int> doesn't have such a type, and neither does TypeMatcher<int*>, nor TypeMatcher<int**>, nor TypeMatcher<int***>. But what if TypeMatcher<int****> does? Better keep trying...
Also remember that partial and complete specialisation exists, meaning that TypeMatcher itself could be specialised.
In short, there is no way to link an int to a TypeMatcher<X>::type if all you have is the int and not the X.
You should be able to achieve something similar by re-structuring (inverting) TypeMatcher a bit:
template <class T>
struct TypeMatcher2
{
static constexpr specialised = false;
};
template <class T>
struct TypeMatcher2<std::shared_ptr<T>>
{
static constexpr specialised = true;
using OldType = T*;
};
template <class T>
struct TypeMatcher2<std::vector<T>>
{
static constexpr specialised = true;
using OldType = T&;
}
template <class T, bool spec = TypeMatcher2<T>::specialised>
struct MyNeedfullTemplate
{
// generic version
};
template <class T>
struct MyNeedfullTemplate<T, true>
{
using OriginalT = typename TypeMatcher2<T>::OldType;
// specialised version
};
I think what you're trying to do is this:
#include <iostream>
#include <memory>
#include <vector>
#include <utility>
template <typename T>
struct TypeMatcher;
template <typename T>
struct TypeMatcher<T *>
{
// making some type from T
typedef std::shared_ptr<T> type;
};
template <typename T>
struct TypeMatcher<T&>
{
// making other type from T
typedef std::vector<T> type;
};
template <typename T, typename = void>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<TypeMatcher<T>, std::enable_if_t<std::is_same<typename TypeMatcher<T>::type, std::vector<std::remove_reference_t<T>>>::value>>
{
static void report() { std::cout << "hello" << std::endl; }
};
int main()
{
using matcher_type = TypeMatcher<int&>;
using full_type = MyNeedfullTemplate<matcher_type>;
full_type::report();
return 0;
}
Do I understand the question correctly?
Given a container, for example a std::list<T> or std::vector<T>, I want to generate a new type std::list<NewT> or std::vector<NewT> respectively in a situation I do not know the container (std::list or std::vector) in advance.
My current (but failing) attempt looks as follows:
#include <list>
#include <vector>
template<class Cont, class NewT> struct SameContNewT : public std::false_type
{};
template<class T, class Alloc, class NewT>
struct SameContNewT<std::list<T, Alloc>, NewT>
{ typedef typename std::list<NewT> type; };
template<class T, class Alloc, class NewT>
struct SameContNewT<std::vector<T, Alloc>, NewT>
{ typedef typename std::vector<NewT> type; };
int main()
{
typedef std::list<int> IntList;
typedef std::list<float> FloatList;
typedef SameContNewT<IntList, float> FloatListToo;
static_assert(std::is_same<FloatListToo, FloatList>::value, "No.");
}
How can the desired behavior be achieved?
Some template template parameter twiddling later...
template <class T, class NewP>
struct SameContNewT;
template <template <class...> class T, class... TPs, class NewP>
struct SameContNewT<T<TPs...>, NewP> {
using type = T<NewP>;
};
Live on Coliru
This does throw away all template arguments of the original type, such as allocators for standard containers. Something more involved would be needed for these, since you can't reuse T's allocator, which is most probably std::allocator<typename T::value_type> and is not fit for allocating NewT's.
The type you want isn't SameContNewT<IntList, float> but its nested type type. You can fix that directly using
typedef SameContNewT<IntList, float>::type FloatListToo;
... or using an alias template:
template <typename Cont, typename New>
using SameContNew = typename SameContNewT<Cont, New>::type;
... and dropping the T.
If you want to get fancy and do the trick for a mostly arbitrary container, you can also use something like that:
template <template <typename...> class Cont, typename... T, typename New>
struct SameContNewT<Cont<T...>, New>
{ typedef Cont<New> type; };
However, using this specialization just makes the approach more general. It doesn't fix the original problem.
template<class Z, class...Ts>
struct rebind_types;
template<class Z, class...Ts>
using rebind_types_t=typename rebind_types<Z,Ts...>;
template<template<class...>class Z, class...Us, class...Ts>
struct rebind_types<Z<Us...>, Ts...>{
using type=Z<Ts...>;
};
now we get:
typedef std::list<int> IntList;
typedef std::list<float> FloatList;
typedef rebind_types_t<IntList, float> FloatListToo;
static_assert(std::is_same<FloatListToo, FloatList>::value, "No.");
and we can pass more than one type argument to rebind_types_t. We "gut" the template instance passed, and give it new arguments.
For a container-specific version, you'd have to understand what parameters where allocators etc (allocators have ways to be rebound to new types). And dealing with things like comparators and hashes for associative containers get tricky.
So I have the following case, apologies for the long example, but it should compile correctly:
#include <tuple>
#include <functional>
#include <iostream>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/vector.hpp>
namespace mpl = boost::mpl;
namespace aux
{
template <typename ...Args>
struct to_vector
{ };
template <typename T, typename ...Args>
struct to_vector<T, Args...>
{ typedef typename mpl::push_front<typename to_vector<Args...>::type, T>::type type; };
template <typename T>
struct to_vector<T>
{ typedef typename mpl::vector<T> type; };
template <>
struct to_vector<>
{ typedef typename mpl::vector<> type; };
template <typename Dest, typename T>
struct tuple_adder
{
typedef decltype(std::tuple_cat(std::declval<Dest>(), std::make_tuple(std::declval<T>()))) type;
};
}
struct foo
{
struct storage
{ };
template <typename T>
struct placeholder : storage
{
placeholder(T&& t) : content(t)
{ }
T content;
};
storage* data;
template <typename ...Args>
foo(Args&&... args)
: data()
{
typedef typename mpl::fold<
typename aux::to_vector<Args...>::type,
std::tuple<>,
aux::tuple_adder<mpl::_1, mpl::_2>
>::type tuple_type;
// Instantiate the tuple
data = new placeholder<tuple_type>(std::make_tuple(std::forward<Args>(args)...));
}
template <typename ...Args>
void set(Args&&... args)
{
typedef typename mpl::fold<
typename aux::to_vector<Args...>::type,
std::tuple<>,
aux::tuple_adder<mpl::_1, mpl::_2>
>::type tuple_type;
auto tp = static_cast<placeholder<tuple_type>*>(data);
*tp = std::make_tuple(std::forward<Args>(args)...);
}
};
int main()
{
foo f(1, 2., std::string("Hello"));
f.set(4, 3., std::string("Bar"));
f.set(3., std::string("Bar"), 3.);
}
The idea is simple, foo utilises type erasure to store a tuple that is constructed via the constructor. Then the restriction should be that set is only allowable where the tuple generated from the variadic argument list matches the held tuple (which has unfortunately had it's type erased.)
Now I can detect this at runtime using typeid(), however, I'd like to know if there is a way I can have the same detection at compile time. The only constraints are that foo cannot be templated, and variant is out as I want to allow foo to be constructed with the fields as necessary (all specified at compile time...)
I fear the answer is that this is not possible (due to the type erasure), however I'm hoping for some ideas of a way to achieve this functionality...
The point of a compile time type system is that it constrains the allowable operations on values of types. If two objects are the same type then they admit the same allowable operations.
So no, there's no way for the compiler to know what you want to allow, given that you've erased the distinction.
Is there a way to do this with some c++11 or at most a boost library?
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T> class remove_all_pointers{
public:
typedef T type;
};
template <typename T> class remove_all_pointers<T*>{
public:
typedef typename remove_all_pointers<T>::type type;
};
int main(){
//correctly prints 'i' on gcc
cout<<typeid(remove_all_pointers<int****>::type).name()<<endl;
}
That doesn't quite work for all pointer types. You need to account for different cv-qualifiers as well:
template <typename T> class remove_all_pointers<T* const>{
public:
typedef typename remove_all_pointers<T>::type type;
};
template <typename T> class remove_all_pointers<T* volatile>{
public:
typedef typename remove_all_pointers<T>::type type;
};
template <typename T> class remove_all_pointers<T* const volatile >{
public:
typedef typename remove_all_pointers<T>::type type;
};
Since C++17 you can create a readable, simple and cv-qualifier aware meta function.
Use it like:
int main()
{
remove_all_pointers_t<int* const* volatile* const volatile*> v = 42;
return 0;
}
C++20
#include <type_traits>
template<typename T>
struct remove_all_pointers : std::conditional_t<
std::is_pointer_v<T>,
remove_all_pointers<
std::remove_pointer_t<T>
>,
std::type_identity<T>
>
{};
template<typename T>
using remove_all_pointers_t = typename remove_all_pointers<T>::type;
C++17
In C++17 std::type_identity isn't available yet and std::identity isn't available anymore, hence you need to create your own 'identity' meta function:
#include <type_traits>
// your custom 'identity' meta function
template <typename T>
struct identity
{
using type = T;
};
template<typename T>
struct remove_all_pointers : std::conditional_t<
std::is_pointer_v<T>,
remove_all_pointers<
std::remove_pointer_t<T>
>,
identity<T>
>
{};
template<typename T>
using remove_all_pointers_t = typename remove_all_pointers<T>::type;
Neither Boost nor C++11 features such a trait template. But your code should work.
I would like to create a construct similar to std::iterator_traits::value_type that can work seamlessly for all types using the same syntax. Imagine we have the following:
template <typename T>
struct value_type {
typedef T type;
};
#define VALUE_TYPE(T) typename value_type<T >::type
This will work for POD types. I can specialize it for my own class:
struct MyClass {
typedef float value_type;
};
template <>
struct value_type<MyClass> {
typedef MyClass::value_type type;
};
though I would prefer to avoid extra value_type instantiations in an ideal world.
The problem is with STL iterators. I need a specialization that gets me to the iterator hierarchy. This fails because the compiler chooses the base case:
template <>
struct value_type<std::_Iterator_base_aux> { // MSVC implementation
typedef value_type type;
};
Choosing a class higher up the hierarchy (_Iterator_with_base would be most natural because that is where value_type is defined) fails because it requires specifying all the iterator traits as template arguments.
Is what I'm trying to do even possible in C++?
You can use SFINAE to detect the presence of the value_type typedef. No need to specialize for individual types (which might not be possible, since you'd be relying entirely on internal implementation details).
#include <vector>
template <class T>
struct has_value_type
{
typedef char true_type;
typedef char false_type[2];
//template not available if there's no nested value_type in U's scope
template <class U>
static true_type test(typename U::value_type* );
//fallback
template <class U>
static false_type& test(...);
//tests which overload of test is chosen for T
static const bool value = sizeof(test<T>(0)) == sizeof(true_type);
};
template <class T, bool b>
struct value_type_impl;
template <class T>
struct value_type_impl<T, false> //if T doesn't define value_type
{
typedef T type;
};
template <class T>
struct value_type_impl<T, true> //if T defines value_type
{
typedef typename T::value_type type;
};
template <class T>
struct value_type: value_type_impl<T, has_value_type<T>::value>
{
};
struct MyClass {
typedef float value_type;
};
template <class T>
int foo(T )
{
return typename value_type<T>::type();
}
int main()
{
foo(MyClass());
std::vector<int> vec;
foo(vec.begin());
foo(10);
}
UncleBens has used SFINAE, but there is actually simpler:
template <class T>
struct value_type
{
typedef typename T::value_type type;
};
Now, if you want to use it with a class you control, the easiest way is:
struct MyClass { typedef float value_type; };
BOOST_MPL_ASSERT((boost::is_same< MyClass::value_type,
typename value_type<MyClass>::type >));
And if you want to use for a class you do not control, you still have specialization:
struct ThirdPartyClass {};
template <>
struct value_type<ThirdPartyClass> { typedef int type; }
If you try to use value_type for a class that does not have an inner typedef and for which no specialization is available, it's a compilation error (and a message you are not likely to understand at first glance...)