std::remove_const with const references
std::remove_const
removes top level const
-qualifications. In const T&
, which is equivalent to T const&
, the qualification is not top-level: in fact, it does not apply to the reference itself (that would be meaningless, because references are immutable by definition), but to the referenced type.
Table 52 in Paragraph 20.9.7.1 of the C++11 Standard specifies, regarding std::remove_const
:
The member typedef type shall name the same type as
T
except that
any top-level const-qualifier has been removed.
[ Example:remove_const<const volatile int>::type
evaluates tovolatile int
, whereasremove_const<const int*>::type
evaluates
toconst int*
. — end example ]
In order to strip const
away, you first have to apply std::remove_reference
, then apply std::remove_const
, and then (if desired) apply std::add_lvalue_reference
(or whatever is appropriate in your case).
NOTE: As Xeo mentions in the comment, you may consider using an alias template such as Unqualified
to perform the first two steps, i.e. strip away the reference, then strip away the const
- (and volatile-
) qualification.
Why doesn't std::remove_const remove const qualifier?
Note that *first
is an lvalue expression, then the result type of decltype(*first)
would be const int&
, i.e. a reference to const int
. The reference is not const
itself (it can't be const-qualified, there's no such thing like int& const
), using std::remove_const
on it will yield the same type, i.e. const int&
.
See decltype specifier:
- If the argument is any other expression of type
T
, andb) if the value category of expression is lvalue, then
decltype
yieldsT&
;
You could use std::remove_const
with std::remove_reference
together:
std::remove_const<std::remove_reference<deref>::type>::type // -> int
^^^^^ // -> const int &
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -> const int
BTW:
Note that I use
std::thread
just to get readable types in the errors:
Note that it doesn't give the correct type for this case. Here's a class template helper for this from the Effective Modern C++ (Scott Meyers):
template<typename T>
class TD;
and use it as
TD<deref> td;
You'll get the error message containing the type of deref
, e.g. from clang:
prog.cc:16:11: error: implicit instantiation of undefined template 'TD<const int &>'
TD<deref> td;
^
Why does using std::remove_reference and std::remove_const in different order produce different results?
remove_const
will only remove a top-level const
qualifier, if one exists. In const std::string&
, the const
is not top-level, hence applying remove_const
has no effect on it.
When you reverse the order and apply remove_reference
first, the resulting type is const string
; now the const
is top-level and the subsequent application of remove_const
will remove the const
qualifier.
Get base type of a template type (remove const/reference/etc.)
I would probaby define a type alias such as:
template<typename T>
using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
Note that, in an article no longer available, R. Martinho Fernandes proposed the name Unqualified
for such a type alias.
The standard type trait std::decay
, on the other hand does the same as the above and something more for array and function types, which may or may not be what you want.
How to remove const ref modifiers for each element in typename... T
The essential problem is pack expansion. Also, you can use std::decay(c++11) or std::decay_t (c++14) from type_traits for simplicity. The following code should compile with c++14.
#include <tuple>
#include <type_traits>
// new variadic version
namespace variadic
{
template < typename F, typename... Args >
void exec(F* pObj, void(F::*pFct)(Args... args))
{
std::tuple<std::decay_t<Args>...> tN;
// some code that fills tN
// some helper template that 'extracts' the tuple and calls (pObj->*pFct)(ExtractedArgs...)
}
}
struct Test
{
void foo(int i) {}
void bar(const float& f, const int& i) {}
void buu(const float& f, int i) {}
};
int main(int argc, char* argv[])
{
Test t;
variadic::exec(&t, &Test::foo); // ok
variadic::exec(&t, &Test::bar); // ok
variadic::exec(&t, &Test::buu); // ok
return 0;
}
Remove reference with const references
template<class T> struct remove_all { typedef T type; };
template<class T> struct remove_all<T*> : remove_all<T> {};
template<class T> struct remove_all<T&> : remove_all<T> {};
template<class T> struct remove_all<T&&> : remove_all<T> {};
template<class T> struct remove_all<T const> : remove_all<T> {};
template<class T> struct remove_all<T volatile> : remove_all<T> {};
template<class T> struct remove_all<T const volatile> : remove_all<T> {};
//template<class T> struct remove_all<T[]> : remove_all<T> {};
//template<class T, int n> struct remove_all<T[n]> : remove_all<T> {};
I originally also stripped extents (arrays), but Johannes noticed that this causes ambiguities for const char[]
, and the question doesn't mention them. If we also want to strip arrays (see also ideas mentioned in the comments), the following doesn't complicate things too much:
#include <type_traits>
template<class U, class T = typename std::remove_cv<U>::type>
struct remove_all { typedef T type; };
template<class U, class T> struct remove_all<U,T*> : remove_all<T> {};
template<class U, class T> struct remove_all<U,T&> : remove_all<T> {};
template<class U, class T> struct remove_all<U,T&&> : remove_all<T> {};
template<class U, class T> struct remove_all<U,T[]> : remove_all<T> {};
template<class U, class T, int n> struct remove_all<U,T[n]> : remove_all<T> {};
or with a helper class but a single template parameter:
#include <type_traits>
template<class T> struct remove_all_impl { typedef T type; };
template<class T> using remove_all =
remove_all_impl<typename std::remove_cv<T>::type>;
template<class T> struct remove_all_impl<T*> : remove_all<T> {};
template<class T> struct remove_all_impl<T&> : remove_all<T> {};
template<class T> struct remove_all_impl<T&&> : remove_all<T> {};
template<class T> struct remove_all_impl<T[]> : remove_all<T> {};
template<class T, int n> struct remove_all_impl<T[n]> : remove_all<T> {};
It is normal if all the variants start looking about the same ;-)
std::remove_reference_tstd::remove_cv_tT does the order matter?
There are cases when these two type traits produce different results. For example, let's consider T = const int&
.
std::remove_cv_t
will remove top-level cv-qualifier, turningconst int&
intoconst int&
, because there is no top-level cv-qualifier.std::remove_reference_t
will then returnconst int
.In the second case,
std::remove_reference_t
will returnconst int
, andstd::remove_cv_t
will transform it intoint
.
Simple demo
clang-tidy suggest I remove const references, why?
The rationale given here is
With move semantics added to the language and the standard library updated with move constructors added for many types it is now interesting to take an argument directly by value, instead of by const-reference, and then copy. This check allows the compiler to take care of choosing the best way to construct the copy.
Additionally
The transformation is usually beneficial when the calling code passes an rvalue and assumes the move construction is a cheap operation.
However, the documentation states that the only replacement is in the following specific case:
Replaces the uses of const-references constructor parameters that are copied into class fields. The parameter is then moved with std::move().
It doesn't even apply the transformation if the constructor parameter is used more than once.
So I don't think all your functions should have been transformed like that.
Related Topics
Use-Cases of Pure Virtual Functions with Body
Debug Assertion Failed! Expression: _Acrt_First_Block == Header
Template Metaprogramming - Difference Between Using Enum Hack and Static Const
Determining the Alignment of C/C++ Structures in Relation to Its Members
Finding Square Root Without Using Sqrt Function
"Field Has Incomplete Type" Error
Namespaces and Operator Resolution
C++ Template Typename Iterator
When Virtual Inheritance Is a Good Design
Convert a C++ Program to a Windows Service
Adding Static Libcurl to Code::Blocks Ide
How to Declare Constexpr Class in a Header and Define It in a Separate .Cpp File
C++ Floating Point to Integer Type Conversions
Is the Typedef-Name Optional in a Typedef Declaration