Template metaprogramming with reference
I tested some cpp book template metaprogramming solutions for the first exercise http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?CPPTM_Answers_-_Exercise_2-0
Write a unary metafunction add_const_ref that returns T if it is a reference type and otherwise returns T const &
template<typename T>
struct add_const_ref
{
typedef typename boost::add_const<T>::type ct;
typedef typename boost::add_reference<ct>::type type;
};
I revisited it with C ++ 11:
template<typename T>
struct add_const_ref_type
{
typedef typename std::add_const<T>::type ct;
typedef typename std::add_lvalue_reference<ct>::type type;
};
I don't understand why it works with the link. I expect this to add const, i.e. Will change int&
to `const int &.
int main()
{
std::cout << std::is_same<add_const_ref_type<int &>::type, int&>::value << '\n'; // print 1
std::cout << std::is_same<add_const_ref_type<int &>::type, const int&>::value << '\n'; // print 0
return 0;
}
source to share
You've been tricked into placing your type modifiers on the left.
const int&
I mean, it looks like when you apply const
to int&
, you are getting it const int&
right?
Wrong.
To understand what's going on here, apply the type modifiers on the right . The modifier always refers to the item to the right of it; at the moment, the claim that puts it "on the left" is illegal:
int const&
it is an int reference, which is a constant. This is really valid C ++.
int& const
it is absurd; you cannot apply const
to &
. &
themselves are already unchanged; only what they refer to can be changed.
When you are add_const<int&>
, you logically receive int& const
; the devil knows that this is nonsense, so it returns int&
.
Now we step back and see what happens if you place it on the left. If there is nothing on the left side, a special rule makes it apply to the leftmost one. So:
const int&
just
int const&
and
using X=int&;
const X
is an
X const
again, according to the rule "left to right of the leftmost part". After that, we expand X
:
int& const
It would be quite obvious that you always apply the type modifiers on the right and ignore the special case for the type modifiers on the left.
Fixing this, if you want to add const to value types and reference references to a const instance, it is easy to override it, but it is difficult to solve overall. After all, a link is a kind of alias, just like a pointer; when you add const
to a pointer you get a const pointer, not a const pointer.
I suspect the assignment allows you to solve the problem from scratch rather than using std
or boost
.
template<class T>
struct tag_t {using type=T;};
template<class T>
struct add_const_ref:tag_t<T>{};
template<class T>
struct add_const_ref<T&>:tag_t<T const&>{};
source to share
This works because it int & const
doesn't make any sense (the reference is always const), so it is the std::add_const<int &>::type
same as int &
. That is, the part that is being executed const
is not int
.
Here's an example:
#include <iostream>
#include <type_traits>
int main() {
std::cout << std::is_same<int &, std::add_const<int &>::type>::value << std::endl;
std::cout << std::is_same<int, std::add_const<int>::type>::value << std::endl;
std::cout << std::is_same<int const, std::add_const<int>::type>::value << std::endl;
std::cout << std::is_same<int &, std::add_lvalue_reference<std::add_const<int &>::type>::type>::value << std::endl;
std::cout << std::is_same<int &, std::add_lvalue_reference<std::add_const<int>::type>::type>::value << std::endl;
std::cout << std::is_same<int const &, std::add_lvalue_reference<std::add_const<int>::type>::type>::value << std::endl;
}
source to share