In the following code:
template <typename U, typename V> class A {};
template <typename U, typename V> class B {};
template <typename T>
class C {
template <typename U, typename V> friend class A; // Works fine.
// template <typename U> friend class B<U,T>; // Won't compile.
};
I want B<U,T> to be friend to C<T>, that is, the second parameter of B must match C's parameter, though its first parameter can be anything. How do I achieve this? The friend declaration of A<U,V> is too much, though I will take that if I can't restrict it any further.
Perhaps define a meta-function
template <typename, typename = void> struct FriendTraits { struct type{}; };
or something like that within C?
The first 2 lines of
template <typename, typename = void> struct FriendTraits { struct type{}; };
template <typename U> struct FriendTraits<U,T> { using type = B<U,T> ; } ;
template <typename U> friend typename FriendTraits<U,T>::type;
compiled, but not the important 3rd line (because it is the same problem).
Related
I have a
template <typename T, typename U>
struct A;
and a
template <typename U>
struct B;
How do I make an alias that transforms an A<T, U> type to a A<T, B<U>> type? i.e. something that looks like this:
template <typename TypeA>
using ModifiedTypeA = TypeA<T, B<U>>; // <--- I don't know how to get T and U from TypeA
where ModifiedTypeA only needs to be templated on TypeA?
I was thinking that the above could be achieved if TypeA is always guaranteed to have member aliases
template <typename T_, typename U_>
struct A
{
using T = T_;
using U = U_;
};
and then do
template <typename TypeA>
using ModifiedTypeA = TypeA<typename TypeA::T, B<typename TypeA::U>>;
But is there another cleaner way + that doesn't make the above assumption?
Try
template <typename TypeA, template<typename> typename TemplateB>
struct ModifiedTypeAWtihImpl;
template <template<typename, typename> typename TemplateA,
typename T, typename U,
template<typename> typename TemplateB>
struct ModifiedTypeAWtihImpl<TemplateA<T, U>, TemplateB> {
using type = TemplateA<T, TemplateB<U>>;
};
template <typename TypeA, template<typename> typename TemplateB>
using ModifiedTypeAWtih = typename ModifiedTypeAWtihImpl<TypeA, TemplateB>::type;
Demo
Here say I have a simple template function that in principle can accept all kind of types:
template <class Type>
std::ostream& operator<< (std::ostream& stream, const Type subject) {
stream << "whatever, derived from subject\n";
return stream; }
I only want to use this template to cout a few types, say std::vector and boost::array objects. However whenever I use cout to other types even elementary types, e.g. std::cout << int(5);, will be a compilation error, because there are two possible implementations of operator<<(std::ostream, int) now, one is in standard c++, the other specified by my template function.
I would like to ask, is it possible to restrict my template function, so that it only accepts a few types specified by me? That is how to tell the compiler to ignore my template when i use cout << int(5). Thanks in advance.
To be more clear, this is what I want to do:
template <class Type>
std::ostream& operator<< (std::ostream& stream, const Type subject) {
if (Type == TypeA or TypeB or TypeC) //use this template and do these {...};
else //ignore this template, and use operator<< provided in standard c++ library.
}
Writing a really generic solution for this is hard. The problem with checking an arbitrary type T against std::vector or std::array is that the latter are not classes, they are class templates. Even worse, std::array is a class template with a non-type template parameter, so you can't even have a parameter pack which will hold both std::vector and std::array.
You can get around this somewhat by explicitly wrapping non-type parameters up in types, but it gets ugly, fast.
Here is a solution I came up with that will support any class or template class with no non-type template parameters by default. Template classes with non-type template parameters can be supported by adding a wrapper type to map non-type parameters to type parameters.
namespace detail{
//checks if two types are instantiations of the same class template
template<typename T, typename U> struct same_template_as: std::false_type {};
template<template<typename...> class X, typename... Y, typename... Z>
struct same_template_as<X<Y...>, X<Z...>> : std::true_type {};
//this will be used to wrap template classes with non-type args
template <typename T>
struct wrapImpl { using type = T; };
//a wrapper for std::array
template <typename T, typename N> struct ArrayWrapper;
template <typename T, std::size_t N>
struct ArrayWrapper<T, std::integral_constant<std::size_t, N>> {
using type = std::array<T,N>;
};
//maps std::array to the ArrayWrapper
template <typename T, std::size_t N>
struct wrapImpl<std::array<T,N>> {
using type = ArrayWrapper<T,std::integral_constant<std::size_t,N>>;
};
template <typename T>
using wrap = typename wrapImpl<typename std::decay<T>::type>::type;
//checks if a type is the same is one of the types in TList,
//or is an instantiation of the same template as a type in TempTList
//default case for when this is false
template <typename T, typename TList, typename TempTList>
struct one_of {
using type = std::false_type;
};
//still types in the first list to check, but the first one doesn't match
template <typename T, typename First, typename... Ts, typename TempTList>
struct one_of<T, std::tuple<First, Ts...>, TempTList> {
using type = typename one_of<T, std::tuple<Ts...>, TempTList>::type;
};
//type matches one in first list, return true
template <typename T, typename... Ts, typename TempTList>
struct one_of<T, std::tuple<T, Ts...>, TempTList> {
using type = std::true_type;
};
//first list finished, check second list
template <typename T, typename FirstTemp, typename... TempTs>
struct one_of<T, std::tuple<>, std::tuple<FirstTemp, TempTs...>> {
//check if T is an instantiation of the same template as first in the list
using type =
typename std::conditional<same_template_as<wrap<FirstTemp>, T>::value,
std::true_type,
typename one_of<T, std::tuple<>, std::tuple<TempTs...>>::type>::type;
};
}
//top level usage
template <typename T, typename... Ts>
using one_of = typename detail::one_of<detail::wrap<T>,Ts...>::type;
struct Foo{};
struct Bar{};
template <class Type>
auto operator<< (std::ostream& stream, const Type subject)
//is Type one of Foo or Bar, or an instantiation of std::vector or std::array
-> typename std::enable_if<
one_of<Type, std::tuple<Foo,Bar>, std::tuple<std::vector<int>,std::array<int,0>>
>::value, std::ostream&>::type
{
stream << "whatever, derived from subject\n";
return stream;
}
Please don't use this, it's horrible.
Live Demo
You can restrict your overload like this:
template <class T>
std::ostream& my_private_ostream( std::ostream& stream, const T& data )
{ <your implementation> }
template <class T, class A>
std::ostream& operator<< ( std::ostream& stream, const std::vector<T,A>& data )
{ return my_private_ostream(stream,data); }
Same for std::arrays (you should tag your question with c++11):
template <class T, size_t N>
std::ostream& operator<< ( std::ostream& stream, const std::array<T,N>& data )
{ return my_private_ostream(stream,data); }
Alternatively, for a solution that looks a bit more like your edit, you could use C++11 enable_if, although I have a personal aversion to them as they tend to make the code difficult to read and maintain. So I strongly recommend the previous solution.
// Vector type predicate
template <class T>
struct is_vector: std::false_type {};
template <class T, class A>
struct is_vector< std::vector<T,A> >: std::true_type {};
// Array type predicate
template <class T>
struct is_array: std::false_type {};
template <class T, size_t N>
struct is_array< std::array<T,N> >: std::true_type {};
// The overload with the syntax you want
template <class Indexable>
typename std::enable_if<
is_vector<Indexable>::value || is_array<Indexable>::value,
std::ostream&
>::type
operator<< ( std::ostream& stream, const Indexable& data )
{ <your implementation> }
Use SFINAE to do what you're asking.
template<typename...>
struct is_vector: std::false_type{};
template<typename T, typename Alloc>
struct is_vector<std::vector<T, Alloc>>: std::true_type{};
template<typename...>
struct is_array: std::false_type{};
template<typename T, std::size_t Size>
struct is_array<std::array<T, Size>>: std::true_type{};
template<typename T>
struct is_my_ostream_type{
enum {
value = is_vector<T>::value || is_array<T>::value
};
};
template<
typename T,
typename = typename std::enable_if<is_my_ostream_type<T>::value>::type
>
std::ostream &operator <<(std::ostream &lhs, const T &rhs){
lhs << "is my special ostream overload";
return lhs;
}
But you're probably going to end up just writing an overload for every type rather than doing this.
Having a class like the A, is there a way to apply it to a template like this of B, with T2 set to some type C? But without creating another template class inheriting from A.
template<typename T1, typename T2>
class A
{ };
template<template <typename T1> class T3>
class B
{ };
With C++11 using a template alias works:
template<typename T1, typename T2>
class A
{ };
template<template <typename T1> class T3>
class B
{ };
class C
{ };
template< typename T > using A1 = A< T, C >;
int main()
{
B< A1 > b;
}
without C++11, you are left with what you probably already know:
template< typename T > class A1 : A< T, C > {};
I will propose an alternative solution: do not use template template parameters.
If you write:
template <typename T> struct B {};
Then it can be used with A<int, int> or C<3> or even plain D.
Whilst it is possible to use template template parameters, it is general a bad idea. You should treat the template parameter of a class as an implementation detail and apply the golden rule: do not rely on implementation details.
If you need access to the type, somehow, then use an associated type (aka T::AssociatedType) or a trait (BTraits<T>::AssociatedType).
EDIT: dealing with multiple instantiations of the template template parameter.
Suppose we want to "erase" the template template parameter of such a class:
template <template <typename> class A>
struct Something {
template <typename T>
void doit() { A<T>::doit(); }
};
The C++ standard allocation model is to use an inner rebind structure:
template <typename T>
struct Simple {
template <typename U>
struct rebind { typedef Simple<U> type; };
};
template <typename T0, typename T1>
struct Multi {
template <typename U>
struct rebind { typedef Multi<U, T1> type; };
};
template <typename A>
struct Something {
template <typename T>
void doit() { typedef typename A::rebind<T>::type B; B::doit(); }
};
Note how you can use complex computations in rebind and nothing forces you in blindly passing the type received as parameter.
Whilst another (similar) solution is to ask for a factory (aka, the object passed itself cannot be used but it can build useful objects); for ease of use the C++ containers ask of their allocators that they be both usable in themselves and factories for other types.
Yes, you can do it using C++11's alias template:
template <typename T>
using AA = A<T, C>;
B<AA> b;
Live example
A beginner question:
How to put a condition on a template in a multi-templated class:
I tried this:
template <class T, class U>
typename std::enable_if<...>
class foo
{
};
And this:
template <class T,
class U = std::enable_if<...>>
class foo
{
};
But they are not working. Any help would be appreciated :)
Declare an additional template parameter defaulted to void and specialize it with enable_if:
template <typename T, typename U, typename Enable = void>
class foo {};
template <typename T, typename U>
class foo<T, U, typename std::enable_if<...>::type>
{
};
Suppose I'm in a template and I want to know if a type parameter T is an instantiation of a particular template, e.g., std::shared_ptr:
template<typename T>
void f(T&& param)
{
if (instantiation_of(T, std::shared_ptr)) ... // if T is an instantiation of
// std::shared_ptr...
...
}
More likely I'd want to do this kind of test as part of a std::enable_if test:
template<typename T>
std::enable_if<instantiation_of<T, std::shared_ptr>::type
f(T&& param)
{
...
}
// other overloads of f for when T is not an instantiation of std::shared_ptr
Is there a way to do this? Note that the solution needs to work with all possible types and templates, including those in the standard library and in other libraries I cannot modify. My use of std::shared_ptr above is just an example of what I might want to do.
If this is possible, how would I write the test myself, i.e., implement instantiation_of?
Why use enable_if when simple overloading suffices?
template<typename T>
void f(std::shared_ptr<T> param)
{
// ...
}
If you really do need such a trait, I think this should get you started (only roughly tested with VC++ 2010):
#include <type_traits>
template<typename>
struct template_arg;
template<template<typename> class T, typename U>
struct template_arg<T<U>>
{
typedef U type;
};
template<typename T>
struct is_template
{
static T* make();
template<typename U>
static std::true_type check(U*, typename template_arg<U>::type* = nullptr);
static std::false_type check(...);
static bool const value =
std::is_same<std::true_type, decltype(check(make()))>::value;
};
template<
typename T,
template<typename> class,
bool Enable = is_template<T>::value
>
struct specialization_of : std::false_type
{ };
template<typename T, template<typename> class U>
struct specialization_of<T, U, true> :
std::is_same<T, U<typename template_arg<T>::type>>
{ };
A partial spec should be able to do it.
template <template <typename...> class X, typename T>
struct instantiation_of : std::false_type {};
template <template <typename...> class X, typename... Y>
struct instantiation_of<X, X<Y...>> : std::true_type {};
http://ideone.com/4n346
I actually had to look up the template template syntax, because I've basically never had cause to use it before.
Not sure how this interacts with templates like std::vector with additional defaulted arguments.
Best way to do it when dealing with a T&& is to make sure you remove_reference before doing the check, because the underlying type T can be a reference or a value type, and template partial specialization has to be exact to work. Combined with an answer above the code to do it could be:
template <
typename T,
template <typename...> class Templated
> struct has_template_type_impl : std::false_type {};
template <
template <typename...> class T,
typename... Ts
> struct has_template_type_impl<T<Ts...>, T> : std::true_type {};
template <
typename T,
template <typename...> class Templated
> using has_template_type = has_template_type_impl<
typename std::remove_reference<T>::type,
Templated
>;
And then you just enable_if your way to victory:
template <typename T>
typename std::enable_if<has_template_type<T, std::shared_ptr>::value>::type
f(T&& param)
{
// ...
}