I was checking some solutions of the book cpp template metaprogramming for the 1st 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 revised 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 do not understand why it works with reference. I expect this will add const, i.e., change the 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;
}
You have been fooled by the great error of placing your type modifiers on the left.
const int&
I mean, it looks like when you apply const to int&, you get const int& right?
Wrong.
To understand what is going on here, apply type modifiers on the right. The modifier always applies to the thing to its right; for now, pretent putting it "on the left" is illegal:
int const&
that is a reference to an int that is const. This is actually valid C++.
int& const
that is nonsense; you cannot apply const to a &. & themselves are already immutable; only what they refer to can be changed.
When you add_const<int&>, you logically get int& const; the trait knows this is nonsense, so returns int&.
Now we step back and look at what happens if you put it on the left. If there is nothing on the left, a special rule makes it apply to the leftmost. So:
const int&
is just
int const&
and
using X=int&;
const X
is
X const
again, by the "on the left is just on the right of the leftmost part" rule. After this is applied, we then expand X:
int& const
This would be quite obvious is you always applied type modifiers on the right, and ignored the special case for type modifers on the left.
Fixing this, if you want to add const to value types and make references refer to a const instance, is easy to patch over, but solving it generically is hard. After all, a reference is a kind of alias, as is a pointer; when you add const to a pointer, you get a const pointer, not a pointer to const.
I suspect the assignment wants you to solve the problem from scratch, not use 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&>{};
It works because int & const does not make any sense (a reference is always const), thus std::add_const<int &>::type is the same as int &.
That is, the part that is being made const is not int.
Here is 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;
}
Online here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With