Forwarding references and template templates - c++

Consider these two template functions:
template<typename T>
void foo(T&& bar) {
// do stuff with bar, which may or may not be an instance of a templated class
}
template<typename U, template<typename> class T>
void foo(T<U>&& bar) {
// do stuff with bar, which must be an instance of a templated class
}
Why does the former accept lvalues (by using a forwarding reference) while the latter does not?
It looks like Can an identity alias template be a forwarding reference? may be related to this as well, but it seems to cover a different facet of the restrictions on forwarding references.

If you want to retain a forwarding reference parameter, and, at the same time, deduce the type of an argument, you can use the below solution:
#include <type_traits>
#include <utility>
template <typename T>
struct tag {};
template <typename T, typename U, template <typename> class C>
void foo(T&& t, tag<C<U>>)
{
}
template <typename T>
auto foo(T&& t)
-> decltype(foo(std::forward<T>(t), tag<typename std::decay<T>::type>{}))
{
return foo(std::forward<T>(t), tag<typename std::decay<T>::type>{});
}
DEMO

Because that's how the standard says the language should work.
[14.8.2.1][temp.deduct.call]
3.If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a
reference type, the type referred to by P is used for type deduction. A forwarding reference is an rvalue
reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Only an rvalue-reference to a CV-unqualified template parameter can be deduced as an l-value reference in this manner.
To achieve what you are trying to do, you might be able to use a trait to extract the template template parameter.
#include <type_traits>
/***
* Extract template from template type.
*/
template <typename I> struct get_template;
template <template<class> typename T, typename C>
struct get_template<T<C>> {
template <typename U>
using temp = T<U>;
};
template <typename T> struct A{};
struct B;
template<typename W>
void foo(W && bar) {
typedef typename get_template<typename std::remove_reference<W>::type>::template temp<int> new_type;
new_type my_variable;
}
int main() {
A<B> temp;
foo(temp);
}
Or, just overload the function for const & and && as usual.

Related

Deduce template parameter from concept

I'm learning templates and concepts. I'm trying to make a concept for types that are derived from a class, but this class is a template.
template<typename T>
struct CAA{};
template<typename T, typename T2>
concept DerivedFromAA = requires() {std::derived_from<CAA<T2>,T>;};
Is it possible to use such concept in a function without having to explicitly tell it the template type of the class? Is my idea wrong on how to define such concept?
template<typename T>
void conceptTestFunc(DerivedFromAA<T> auto& aa)
{
}
//...
CAA<int> aa;
conceptTestFunc<int>(aa); // Without having to tell it "int"
(I'm compiling this with Clang.)
A template is not a type.
So if CAA is a template, then CAA<int> would be a type.
A type can't be derived from a template, only from another type. This means that the check has to be done on the type, not the template.
If you on the other hand want to deduce the inner type of aa, that can be done.
#include <concepts>
template<typename T>
struct CAA{};
template<typename T>
struct CBB{};
template<typename T, typename T2>
concept DerivedFromAA = std::derived_from<CAA<T2>,T>;
template<template <typename> typename Outer, typename T>
requires DerivedFromAA<Outer<T>, T>
void conceptTestFunc(Outer<T>& aa)
{
}
int main() {
CAA<int> aa;
conceptTestFunc(aa);
CBB<int> bb;
conceptTestFunc(bb); // This fails
}
You might do
template<typename T>
concept DerivedFromAA = requires(T t) {[]<typename U>(CAA<U>&){}(t);};
static_assert(DerivedFromAA<CAA<int>>);
Demo
gcc dislikes lambda in requires, so you might create dummy helper function outside.

How to declare a variable whose type is the generics of another object?

Consider the following piece of C++ code:
int main(){
MyObject<int> obj;
foo(obj);
}
template <typename T>
void foo(T& objct){
...
}
In foo, the type of objct will be MyObject<int>.
I would like to create a variable in foo() whose type is the objct's generics, in this case, int.
Is there a way to do that? Thank you.
Edit
Unfortunately (I think) I can't rewrite the signature because the function foo() is called with different type of objects, for example
int main(){
MyObject<int> obj;
MyDifferentObject<int> obj2;
foo(obj);
foo(obj2);
}
What about defining foo() using a template-template parameter?
template <template <typename...> class C, typename T>
void foo (C<T> & objct)
{
/...
}
or also
template <template <typename...> class C, typename T, typename ... Ts>
void foo (C<T, Ts...> & objct)
{
/...
}
to be more flexible and accept also type with multiple template types parameters.
This way, if you call
MyObject<int> obj;
MyDifferentObject obj2;
foo(obj);
foo(obj2);
you have that C is MyObject in first case, MyDifferentObject in the second case and T is int in both cases.
This, obviously, works only if the argument of foo() are object of a template class with only template type parameters so, for example, doesn't works for std::array
std::vector<int> v;
std::array<int, 5u> a;
foo(v); // compile: only types parameters for std::vector
foo(a); // compilation error: a non-type template parameter for std::array
I would like to create a variable in foo() whose type is the objct's generics, in this case, int.
Is there a way to do that?
If you can change the function signature, then you can do this:
template <typename T>
void foo(MyObject<T>& objct){
T variable;
If that is not an option, for example if you want foo to allow other templates too (such as in your edited question), then you can define a type trait:
template<class T>
struct fancy_type_trait
{
};
template<class T>
struct fancy_type_trait<MyObject<T>>
{
using type = T;
};
template<class T>
struct fancy_type_trait<MyDifferentObject<T>>
{
using type = T;
};
template <typename T>
void foo(T& objct){
using V = typename fancy_type_trait<T>::type;
V variable;
You can write a trait that determines the first template parameter of any instantiation of a template with one template parameter:
#include <type_traits>
template <typename T>
struct MyObject {};
template <typename T>
struct MyOtherObject {};
template <typename T>
struct first_template_parameter;
template <template<typename> typename T,typename X>
struct first_template_parameter< T<X> > {
using type = X;
};
int main() {
static_assert(std::is_same< first_template_parameter<MyObject<int>>::type,
first_template_parameter<MyOtherObject<int>>::type>::value );
}
The trait first_template_parameter can take any instantiation of a template with a single parameter and tells you what that parameter is. first_template_parameter< MyObject<int> >::type is int. More generally first_template_parameter< SomeTemplate<T> >::type is T (given that SomeTemplate has one parameter).
This is a slight generalization of the trait used in this answer and if needed it could be generalized to also work for instantiations of tempaltes with more than one parameter.
In your function you would use it like this:
template <typename T>
void foo(T& objct){
typename first_template_parameter<T>::type x;
}

Using template typename in a nested function template

Alright, I'm struggling with templates. In this question I learned, that it is not possible to pass a type specifier to a function at all, so my next approach is passing the type inside <>.
Imagine a function template foo<U>(), which is member function of a template class A. So if I create an Object A<T> a I can call a.foo<U>() with any type.
How do I have to write an equivalent function template so I can pass a and U like wrappedFoo<U>(a)?
IMPORTANT Needs to be C++98 compliant
You might do the following:
template <typename U, typename T>
XXXX /* See below */
WrappedFoo(/*const*/ A<T>& a)
{
return a.template foo<U>();
}
The hard part is the return type without decltype of C++11.
So if the return type really depends of parameters type, you can create a trait, something like:
template <typename U, typename T>
struct Ret
{
typedef U type;
};
template <typename T> struct Ret<T, A<T> >
struct Ret
{
typedef bool type;
};
And then replace XXXX by typename Ret<U, T>::type

Confusing C++ Template

I am studying C++ Templates. Can someone plaese explain every bit of this piece of code
template <class T>
struct identity
{
typedef T type;
};
template <class T>
T&& forward(typename identity<T>::type&& a)
{
return a;
}
template <class T>
struct identity
{
typedef T type;
};
This part is defining a class template named identity that holds a public member typedef named type of the type you pass as the template argument. In your example there are no partial or explicit specializations so any type that is passed to identity is type.
template <class T>
T&& forward(typename identity<T>::type&& a)
{
return a;
}
forward is a function template taking an rvalue-reference to the type returned by identity<T>::type. The type returned by type (however obvious it may be) cannot be deduced by the compiler to be T (because the type is a dependent type), so you must explicitly specify the template argument for forward.
The rvalue-reference syntax && (for the return-type) also denotes what is (informally) referred to as a universal reference since the type T is a template argument. This means the return-type can bind to both rvalues and lvalues returned by the function.
The parameter type identity<T>::type&& is not a universal reference because the type returned is not a template parameter. This means the parameter can only accept rvalues. That will require us to move lvalues into the parameter to forward:
int main()
{
int n{0};
forward<int>(std::move(n));
}
And in the end we return the parameter a to the rvalue reference. Note however that returning the parameter to T&& won't work because a will have to be moved:
template <class T>
T&& forward(typename identity<T>::type&& a)
{
return std::move(a);
}
Otherwise return an lvalue-reference instead:
template <class T>
T& forward(typename identity<T>::type&& a)
{
return a;
}
Firstly, you need another specialization for my_forward to allow this call:
int a;
my_forward<int>(a);
So, specialize my_forward for references like this:
template <class T>
T&& my_forward(typename identity<T>::type& a)
{
return static_cast<T&&>(a);
}
But in this case, call for
int a;
my_forward<int&>(std::ref(a));
is ambiguous:
note: candidate function [with T = int &]
T&& my_forward(typename identity<T>::type&& a)
^
note: candidate function [with T = int &]
T&& my_forward(typename identity<T>::type& a)
^
To avoid it, you should use std::remove_reference instead of just identity:
template <class T>
T&& my_forward(typename std::remove_reference<T>::type&& a)
{
return static_cast<T&&>(a);
}
template <class T>
T&& my_forward(typename std::remove_reference<T>::type& a)
{
return static_cast<T&&>(a);
}

How do template aliases affect template parameter deduction?

In C++03, template parameter deduction does not occur in some contexts. For example:
template <typename T> struct B {};
template <typename T>
struct A
{
typedef B<T> type;
};
template <typename T>
void f(typename A<T>::type);
int main()
{
B<int> b;
f(b); // ERROR: no match
}
Here, int is not deduced for T, because a nested type such as A<T>::type is a non-deduced context.
Had I written the function like this:
template <typename T> struct B {};
template <typename T>
void f(B<T>);
int main()
{
B<int> b;
f(b);
}
everything is fine because B<T> is a deduced context.
In C++11, however, template aliases can be used to disguise a nested type in syntax similar to the second example. For example:
template <typename T> struct B {};
template <typename T>
struct A
{
typedef B<T> type;
};
template <typename T>
using C = typename A<T>::type;
template <typename T>
void f(C<T>);
int main()
{
B<int> b;
f(b);
}
Would template argument deduction work in this case? In other words, are template aliases a deduced context or a non-deduced context? Or do they inherit the deduced/non-deduced status of whatever they alias?
In other words, are template aliases a deduced context or a non-deduced context?
They are as deducible as the equivalent code without using template aliases. For example
template<typename T>
using ref = T&;
template<typename T>
void f(ref<T> r);
Now you can call f(x) and T will be deduced perfectly fine. At the definition time of f already, ref<T> is replaced by type T&. And T& is a deduced context.
In your case C<T> is replaced by typename A<T>::type, and that is a non-deduced context for T, so T cannot be deduced.
Imagine this:
template <typename T> struct Foo { typedef T type; }
template <> struct Foo<char> { typedef int type; }
template <typename T> using mytype = typename Foo<T>::type;
template <typename T> void f(mytype<T>);
Now if I want int n; f(n);, how could I decide whether I want T = int or T = char? The whole problem, which is unaffected by template aliases, is that you cannot deduce backwards to all the things that could possibly define something.
I think the relevant quote in the C++ standard is 14.5.7 [temp.alias] paragraph 2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template. [ Note: An alias template name is never deduced. — end note ]
There is an example following the quote which effectively spells out that it is pointless to use an alias template in a function template and hoping to deduce the template argument. This apparently applies even for situation which don't involve nested types.