Confusing C++ Template - c++

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);
}

Related

Template deduction fails for known argument

Consider following code
template<typename T>
T modify(const T& item, std::function<T(const T&)> fn)
{
return fn(item);
}
When trying to use it as modify(5, [](const int& i){return 10*i;}); it fails to compile with
could not deduce template argument for 'std::function<T(const T &)> from lambda
I know that compiler can not deduce T from lambda, because lambda is not std::function, but isn't T already deduced from 5?
I can get over it using
template<typename T, typename F>
T modify(const T& item, const F& functor)
{
return functor(item);
}
for which previous example compiles, but it is in my opinion less intuitive. Is there a way to let the function argument to remain std::function and have it's template argument deduced automatically from item?
What you basically want to do is prevent deduction from happening. If template deduction occurs, it will fail (because a lambda is not a std::function<> - it doesn't matter that T was deduced from the first argument, deduction must succeed in every argument that is a deduced context). The way to prevent deduction is to stick the entire argument in a non-deduced context, the easiest way of doing that is to throw the type into a nested-name-specifier. We create such a type wrapper:
template <class T> struct non_deduce { using type = T; };
template <class T> using non_deduce_t = typename non_deduce<T>::type;
And then wrap the type in it:
template<typename T>
void foo(const T& item, std::function<void(T)> f);
template<typename T>
void bar(const T& item, non_deduce_t<std::function<void(T)>> f);
foo(4, [](int ){} ); // error
bar(4, [](int ){} ); // ok, we deduce T from item as int,
// which makes f of type std::function<void(int)>
Note, however, that:
template <typename T, typename F>
void quux(const T&, F );
is not really any less readable, and strictly more performant.
You can do it by using the identity trick as below:
template <typename T>
struct identity {
typedef T type;
};
template<typename T>
T modify(const T& item, typename identity<std::function<T(const T&)>>::type fn) {
return fn(item);
}
Live Demo

Forwarding references and template templates

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.

Delegate to another object's operator->

Writing at alias template that will deduce the return type of some type T's operator->, so far I have this
template <typename T>
using arrow = decltype(std::declval<T&>().operator->());
which works for all class types, but doesn't work for pointers. A similar problem exists trying to actually call the ->
template <typename T>
struct D {
T t;
arrow<T> f() {
// not valid to call .operator->() on pointers
return t.operator->();
}
};
How can I make this function get the correct return type declared, and delegate correctly for both class types and pointers?
For a pointer, the type of its operator->() its own type, and the resulting object has its same value. With another level of indirection, a helper struct can be specialized for pointer types
template <typename T>
struct ArrowHelper {
using type = decltype(std::declval<T&>().operator->());
type operator()(T& t) const {
return t.operator->();
}
};
template <typename T>
struct ArrowHelper<T*> {
using type = T*;
constexpr type operator()(T* t) const noexcept {
return t;
}
};
To simplify usage, an alias template and a function can be defined easily
template <typename T>
using arrow = typename ArrowHelper<T>::type;
template <typename T>
arrow<T> apply_arrow(T& t) {
return ArrowHelper<T>{}(t);
}
the delegating member function then becomes
template <typename T>
struct D {
T t;
arrow<T> f() { return apply_arrow(t); }
};

Deduction fails on parameter, works on return value

I tried to use std::enable_if on a function parameter to trigger SFINAE. Compilation fails with this error:
type_nonsense.cpp:20:5: error: no matching function for call to 'c'
c(SOME::VALUE);
^
type_nonsense.cpp:13:6: note: candidate template ignored: couldn't infer
template argument 'T'
void c(typename std::enable_if<std::is_enum<T>::value, T>::type t) {}
^
1 error generated.
Moving the std::enable_if to either the return type or to a dummy template parameter works fine. Why?
#include <type_traits>
// Works
template <typename T, typename dummy = typename std::enable_if<std::is_enum<T>::value, T>::type>
void a(T t) {}
// Works
template <typename T>
typename std::enable_if<std::is_enum<T>::value, void>::type b(T t) {}
// Fails to compile
template <typename T>
void c(typename std::enable_if<std::is_enum<T>::value, T>::type t) {}
enum class SOME { VALUE };
int main() {
a(SOME::VALUE);
b(SOME::VALUE);
c(SOME::VALUE);
}
A dependent type in a nested name specifier is a non-deduced context for template argument deduction, and as such cannot be used to determine the type of T. Placing the std::enable_if in either the return type or as a default template parameter works because the type of T isn't being deduced in those contexts.
If you need to place it as a parameter, you can do so like this:
template <typename T>
void c(T t, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr) {}
This works because T is being deduced by the first argument, not the second.
For the one which fails to compile, T is not deductible.

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.