It is known that the following function pointers have different types:
void foo_int_ref(int&);
void foo_const_int_ref(const int&);
static_assert(
!std::is_same<
decltype(foo_int_ref),
decltype(foo_const_int_ref)
>::value,
"Types should be different");
Let's consider this generalization:
template<typename T>
void foo(T t) {}
template<typename T>
struct ref_vs_const_ref {
typedef decltype(foo<T>) foo_T;
typedef decltype(foo<const T>) foo_const_T;
};
using int_ref_vs_const_ref = ref_vs_const_ref<int&>;
static_assert(
!std::is_same<
typename int_ref_vs_const_ref::foo_T,
typename int_ref_vs_const_ref::foo_const_T
>::value,
"Types should be different"); // -- it fails
The last assertion fails. For some reason, the const
is lost for foo_const_T
. But why?
The const
on value parameters does not affect the signature in any shape or form. The same is true when you remove the reference from the non-template declarations. The const
only affects the use of the parameter in the function definition. If you add a reference or a pointer to the type things change and the const
affects the type of the function.
In your nested types the T
is a int&
to which the const
is applied. However, T&
and T& const
are also the same types. I guess your confusion stems from your ill-advised placement of const
to the left: it is more the const
applies to the top-level entity.
The answer of "why" this is happening has been already given. This is a possible way to resolve your problem -- adding "const" to the nested type:
template<typename T, typename = void>
struct add_value_const
{
using type = const T;
};
template<typename T>
using add_value_const_t = typename add_value_const<T>::type;
template<typename T>
struct add_value_const<
T, std::enable_if_t<
std::is_reference<T>::value
>
>
{
using type = std::conditional_t<
std::is_lvalue_reference<T>::value,
add_value_const_t<std::remove_reference_t<T>>&,
add_value_const_t<std::remove_reference_t<T>>&&
>;
};
template<typename T>
struct add_value_const<
T, std::enable_if_t<std::is_pointer<T>::value>
>
{
using type = add_value_const_t<std::remove_pointer_t<T>>*;
};
In your case you would have to use foo<add_value_const<T>>
instead of foo<const T>
.
I used the C++14 syntax, but it could be ported easily to C++11. With C++17 it is even more readable.
You can find the compiling example on godbolt.
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