First I was learning about template template parameters, and I started wondering if I had a vector<vector<int>>, if I could make a template that extracts out the type int from there.
But, in the process of trying to build an example, I can't even get a single-level template parameter template function to work!
#include <iostream>
#include <vector>
template<
template<class> class C2,
class I
>
void for_2d(const C2<I>& container)
{
for( auto j : container ){
std::cout << j;
}
}
int main() {
std::vector<int> cont;
for_2d(cont);
return 0;
}
This produces:
17 : <source>:17:5: error: no matching function for call to 'for_2d'
for_2d(cont);
^~~~~~
8 : <source>:8:6: note: candidate template ignored: substitution failure : template template argument has different template parameters than its corresponding template template parameter
void for_2d(const C2<I>& container)
^
1 error generated.
Compiler exited with result code 1
The thing you are missing is that vector has multiple template arguments (most of them has default value).
You need to prepare your function for this
template<
template<class...> class C2,
class I
>
void for_2d(const C2<I>& container)
{
for( auto j : container ){
std::cout << j;
}
}
Notice the dots after class
+1 for the Bartosz Przybylski's answer, that explain why your example doesn't compile, but you want
extracts out the type int from there
You use auto j : container, so you're using (at least) C++11; so I suggest you the implementation of a specific, and recursive, type traits.
I propose the following firtType
First of all, the generic (not specialized) version (that is the recursion terminal)
template <typename T>
struct firstType
{ using type = T; };
Next the specialization for std::vector and other containers similar container (that receiving a seguence of types)
template <template <typename...> class C, typename T0, typename ... Ts>
struct firstType<C<T0, Ts...>>
{ using type = typename firstType<T0>::type; };
This specialization works with a lot of containers but not with std::array, that receive a type and a number; the following is a specialization for std::array
template <template <typename, std::size_t> class C, typename T, std::size_t N>
struct firstType<C<T, N>>
{ using type = typename firstType<T>::type; };
Other specializations may be required.
The following is a full working example
#include <array>
#include <vector>
#include <type_traits>
template <typename T>
struct firstType
{ using type = T; };
template <template <typename...> class C, typename T0, typename ... Ts>
struct firstType<C<T0, Ts...>>
{ using type = typename firstType<T0>::type; };
template <template <typename, std::size_t> class C, typename T, std::size_t N>
struct firstType<C<T, N>>
{ using type = typename firstType<T>::type; };
int main ()
{
std::vector<int> vi;
std::array<long, 42U> al;
std::vector<std::vector<short>> vvs;
static_assert( std::is_same<typename firstType<decltype(vi)>::type,
int>::value, "!" );
static_assert( std::is_same<typename firstType<decltype(al)>::type,
long>::value, "!" );
static_assert( std::is_same<typename firstType<decltype(vvs)>::type,
short>::value, "!" );
}
Related
Is it possible to capture a template from a template argument i.e. have a nested template specifier with a template argument that holds the template type?
template< typename T, typename Label = std::string>
class foo {
// ...
};
template <
template < typename C, typename T > typename C<T>,
// ...
typename Label = std::string
>
class bar {
// ...
C< foo< T, Label > > A;
};
For instance, I'd like to pass a generic STL container (std::vector< int >) as template argument, but declare a member of the same meta-type (std::vector) but with different value type (foo< int >) i.e. std::vector< foo< int > >. It may seem convoluted, but it would be helpful to not hardcode the type of STL container.
For context, I'm aiming at a generic container adapter/wrapper (in the line of std::stack or std::queue) that provides some higher-level functionality.
Yes, you can just use template specialization:
#include <string>
template<typename T, typename Label = std::string>
class foo {};
template <class T, typename Label = std::string>
class bar;
template <template<class...> class C, typename T, typename Label>
class bar<C<T>, Label> {
C<foo<T, Label>> A;
};
Demo.
The other answer's approach can be generalized as a reusable template rebinder:
template<typename T>
struct rebinder;
template<template<typename...> typename T, typename... Args>
struct rebinder<T<Args...>> {
template<typename... Us>
using rebind = T<Us...>;
};
template<typename T, typename... Us>
using rebound = rebinder<T>::template rebind<Us...>;
// example:
#include <vector>
template<typename T>
struct MyStruct {
rebound<T, float> vec;
};
int main() {
MyStruct<std::vector<int>> x;
static_assert(std::is_same_v<std::vector<float>, decltype(x.vec)>);
}
see on godbolt
I've got a problem like this I am trying to make compile:
#include <vector>
#include <array>
struct Test
{
template <template <typename...> class Container, typename T, typename... Args>
void SetData(const Container<T, Args...>& data)
{
// compiles for vector but not array
// 'void Test::SetData(const Container<T,Args...> &)': could not deduce template argument for 'const Container<T,Args...> &' from 'std::array<float,3>'
}
};
int main()
{
Test test;
std::vector<int> vector{ 1,2,3 };
std::array<float, 3> arr{1.0f, 2.0f, 3.0f};
test.SetData(vector);
test.SetData(arr);
return 0;
}
Essentially I need a function signature that can accept any arbitary STL container (more specifically std::vector and std::array) and I need the type T visible in the function also (i.e int or float in this case). The other types (allocator or std::array size) I dont care about.
What is the correct signature?
What is the correct signature?
As far I know, there isn't possible to write a single correct signature to intercept all containers and only containers.
But if you accept to write a type traits that say if a type is a container and extract the contained type (require a generic version, a partial specialization for std::vector and similar typename ... containers and a specialization for std::array; other specializations can be added) as the following isCnt
template <typename>
struct isCnt : public std::false_type
{ };
// std::array case
template <template <typename, std::size_t> class C,
typename T, std::size_t N >
struct isCnt<C<T, N>> : public std::true_type
{ using containedType = T; };
// other container case
template <template <typename ...> class C,
typename T0, typename ... Ts>
struct isCnt<C<T0, Ts...>> : public std::true_type
{ using containedType = T0; };
you can construct setData() as follows
template ::containedType>
void SetData (C const & data)
{
// code here
}
Observe that setData() is SFINAE enabled (or disabled) by T = isCnt<C>::containedType, that is available only for containers.
The following is a full working example
#include <array>
#include <vector>
#include <iostream>
#include <type_traits>
template <typename>
struct isCnt : public std::false_type
{ };
// std::array case
template <template <typename, std::size_t> class C,
typename T, std::size_t N >
struct isCnt<C<T, N>> : public std::true_type
{ using containedType = T; };
// other container case
template <template <typename ...> class C,
typename T0, typename ... Ts>
struct isCnt<C<T0, Ts...>> : public std::true_type
{ using containedType = T0; };
struct Test
{
template <typename C, typename T = typename isCnt<C>::containedType>
void SetData (C const & data)
{
if ( std::is_same<T, int>::value )
std::cout << "- int case" << std::endl;
else if ( std::is_same<T, float>::value )
std::cout << "- float case" << std::endl;
}
};
int main ()
{
Test test;
std::vector<int> vector{ 1,2,3 };
std::array<float, 3> arr{ { 1.0f, 2.0f, 3.0f } };
test.SetData(vector); // print "int case"
test.SetData(arr); // print "float case"
//test.SetData(0); // compilation error
}
I want to create template like this which can deduce TT class template an T type:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
template < template <typename> class TT, typename T>
T f(TT<T*> & A ){
cout << "it works\n";
return *A[0];
};
int main(){
vector<int*> v;
f(v);
return 0;
}
I have an error (with clang-4.0):
temded2.cpp: In function ‘int main()’:
temded2.cpp:20:21: error: no matching function for call to ‘f(std::vector<int*>&)’
f<std::vector>(v);
^
temded2.cpp:12:3: note: candidate: template<template<class> class TT, class T> T f(TT<T*>&)
T f(TT<T*> & A ){
^
I think that TT should be equal to std::vector and T should be equal to int, what am i doing wrong ?
Your template template-parameter isn't what you think it is. There's more to a std::vector<T> than you think, including default template parameters that you're not accounting for. Luckily, variadic arguments in C++11 will help you solve that problem
#include <iostream>
#include <vector>
template < template <typename, typename...> class TT, typename T, typename... Args>
T f(TT<T*, Args...> & A )
{
std::cout << __PRETTY_FUNCTION__ << '\n';
if (A.size() > 0)
return *(A[0]);
return T();
};
int main()
{
std::vector<int*> v;
f(v);
return 0;
}
Output
T f(TT<T *, Args...> &) [TT = vector, T = int, Args = <std::__1::allocator<int *>>]
Note the Args above. Because those are missing in your very specific template template-parameter expected arg list, there is no match. As you can see, variadic arguments can solve that problem.
std::vector is a class template that takes two template parameters:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
Your f expects a class template with only one template parameter. So it simply doesn't match.
Since vector is a template with two template parameter, one for the type of the elements, and one for the allocator,
template<
class T,
class Allocator = std::allocator<T>
> class vector;
Your temlate parameter B also needs two template parameters, so the function looks like this:
template <template <typename, typename> class V, typename T, typename A>
T f(V<T, A>& arg ){
cout << "it works\n";
return arg[0];
};
Now the function works.
live demo
I try to write a IsLast type traits to check if a given type is the last one in a std::tuple, but the code below does not compile. I know how to get around it but I am curious why the compiler does not like it.I guess there must be some rule on specialization of variadic-template that I am not aware of.
The code is at: https://godbolt.org/g/nXdodx
Error message:
error: implicit instantiation of undefined template
'IsLast<std::tuple<std::__cxx11::basic_string<char>, int>, int>'
There is also a warning on specialization declaration:
warning: class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used
#include <tuple>
#include <string>
/////////This works
template<typename TP, typename T>
struct IsFirst;
template<typename U, typename ...V, typename T>
struct IsFirst <std::tuple<U, V...>, T>
{
enum {value = false};
};
template<typename U, typename ...V>
struct IsFirst <std::tuple<U, V...>, U>
{
enum {value = true};
};
////////This doesn't compile
template<typename TP, typename T>
struct IsLast;
template<typename ...U, typename V, typename T>
struct IsLast <std::tuple<U..., V>, T>
{
enum {value = false};
};
template<typename ...U, typename V>
struct IsLast <std::tuple<U..., V>, V>
{
enum {value = true};
};
int main()
{
using T = std::tuple<std::string, int>;
bool v1 = IsFirst<T, std::string>::value;
bool v2 = IsLast <T, int>::value;
}
In a class template, the parameter pack must come after all other template parameters, so something like this is not allowed.
template<typename U, typename ...V, typename T>
struct IsFirst <std::tuple<U, V...>, T>
{
enum {value = false};
};
In a function template, there can be other template parameters after the pack only if they can be deduced. This is not allowed for class templates because they do not allow deduction.
As observed by Resurrection, variadic template must be in last position.
But there are a lot of other ways to obtain the same result.
The obvious solution is to create a recursive type traits, but I show you a solution based on std::tuple_element
#include <tuple>
#include <iostream>
#include <type_traits>
template <typename C, typename T>
struct IsLast;
template <template <typename ...> class C, typename T, typename ... Ts>
struct IsLast<C<Ts...>, T>
{
using Tl
= typename std::tuple_element<sizeof...(Ts)-1U, std::tuple<Ts...>>::type;
static constexpr bool value { std::is_same<Tl, T>::value };
};
int main ()
{
using T = std::tuple<std::string, int>;
std::cout << IsLast<T, int>::value << std::endl; // print 1
std::cout << IsLast<T, long>::value << std::endl; // print 0
}
Observe that this IsLast works with
T = std::pairs<int, std::string>
and with other template classes.
EDIT: Thanks to the Bogdan comment, the standard citation is the correct one now!
According to temp.deduct.type#9:
If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context
Note that is only in the argument list of the template that the pack must be the last. Template parameter pack does not need to be the last in the template parameter list, this can be the case for template class partial specialization or template functions.
So your first exemple of class template partial specialization is correct:
template<typename U, typename ...V>
struct IsFirst <std::tuple<U, V...>, U>
{
enum {value = true};
}
Because V... is the last argument of tuple.
template<typename ...V,typename U>
struct IsFirst <std::tuple<V..., U>, U>
{
enum {value = true};
};
Because V... is not the last argument of tuple.
There is a better solution:
#include <tuple>
#include <string>
#include <type_traits>
int main()
{
using T = std::tuple<std::string, int>;
constexpr size_t size = std::tuple_size<T>::value;
typedef decltype(std::get<size - 1>(std::declval<T>())) LastType;
static_assert(std::is_same<std::decay_t<LastType>, int>::value, "no");
}
Source code
This is basically a non-recursive std::tuple_element implementation.
Note: To make this non-recursive, you must replace std::make_index_sequence with a non-recursive implementation. I left it with std::make_index_sequence in order to provide a MVCE.
deduct<std::size_t, T> has a specialization of deduct_impl<T> that is generated from the index sequence template argument it receives. It is used in order to deduce the type at index in a variadic type template or tuple.
itp<std::size_t> and itp<std::size_t, T> is an index-type-pair used to expand the variadic indices template with a type variadic template in order to match the generated specialization.
deducer<std::size_t, T...> puts it all together by specializing deduct<std::size_t, T> and deduct_impl<T> by using std::conditional_t to generate the correct specialization.
Basically, for std::tuple<void, int, char>, in order to get the type at index 1, it creates itp_base<0>, itp<1, int>, itp_base<2> and passes it to deduct and deduct_impl.
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t index>
struct itp_base {};
template <std::size_t index, typename T>
struct itp : itp_base<index> {};
template <std::size_t index, typename IndexSequence>
struct deduct;
template <std::size_t index, std::size_t... indices>
struct deduct<index, std::index_sequence<indices...>>
{
template <typename Tuple>
struct deduct_impl;
template <typename T, typename... R>
struct deduct_impl<std::tuple<itp_base<indices>..., itp<index, T>, R...>>
{
using type = T;
};
};
template <std::size_t index, typename... Types>
class deducer
{
private:
static_assert( index < sizeof...( Types ), "deducer::index out of bounds" );
template <typename IndexSequence>
struct deducer_impl;
template <std::size_t... indices>
struct deducer_impl<std::index_sequence<indices...>>
{
using type = typename deduct<index, std::make_index_sequence<index>
>::template deduct_impl
<
std::tuple
<
std::conditional_t
<
std::is_base_of<itp_base<indices>, itp<index, Types>>::value,
itp<index, Types>,
itp_base<indices>
>...
>
>::type;
};
public:
using type = typename deducer_impl<
std::make_index_sequence<sizeof...( Types )>>::type;
};
template <std::size_t index, typename... Types>
using tuple_element_t = typename deducer<index, Types...>::type;
int main()
{
tuple_element_t<3, int, void, char, std::string> s{ "string" };
std::cout << s << '\n';
}
Odd compiler behaviour
Clang++
I'm getting warnings for non-deducible template arguments from Clang++, but the program outputs correctly.
Visual C++ v140
The type is detected correctly. However I get the following warning:
warning C4552: '<<': operator has no effect; expected operator with side-effect
The program does not output anything.
G++
Everything works properly.
Demo
http://coliru.stacked-crooked.com/a/7cb3ac06ab4b2d4c