Why is std::is_const_v evaluated to false here? [duplicate] - c++

This question already has answers here:
template argument deduction for const argument
(2 answers)
Closed 19 days ago.
According to cppreference:
If T is a reference type then is_const<T>::value is always false. The
proper way to check a potentially-reference type for const-ness is to
remove the reference: is_const<typename remove_reference<T>::type>.
That makes sense for references, because there are no const references, there may only be a reference to a constant. But that should not be the case for pointers as they, unlike references, can be const. What if I want to check if a template parameter is a const pointer to a const type?
template<typename T>
requires std::is_pointer_v<T> // Ok
&& std::is_const_v<std::remove_pointer_t<T>> // Ok
// && std::is_const_v<T> // Fails (2)
void foo(T t) { std::cout << *t << '\n'; }
int main()
{
const int i{5};
const int* const p{&i};
static_assert(std::is_const_v<std::remove_pointer_t<decltype(p)>>); // Ok
static_assert(std::is_const_v<decltype(p)>); // Ok (1)
foo(p);
}
Something is wrong here - constraint (2) is evaluated to false. But at the same time static_assert() (1) is evaluated to true. Why is that? Is it somehow related to template parameter deduction?

Is it somehow related to template parameter deduction?
Yes, T t will never deduce T as const. Because to the caller the (top-level) const is completely unnoticeable.

First things first, T is deduced to be const int* for the call foo(p).
Something is wrong here - constraint (2) is evaluated to false. But at the same time static_assert() (1) is evaluated to true. Why is that?
Because std::is_const_v checks for a top-level const but const int* has a low-level const(not a top-level const).
On the other hand, decltype(p) is const int* const which has a top-level const which is what std::is_const_v checks.

Related

Explain std::forward in deep details [duplicate]

This question already has answers here:
Closed 11 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
Possible Duplicate:
Advantages of using forward
I know what it does and when to use it but I still can't wrap my head around how it works. Please be as detailed as possible and explain when std::forward would be incorrect if it was allowed to use template argument deduction.
Part of my confusion is this:
"If it has a name, it's an lvalue" - if that's the case why does std::forward behave differently when I pass thing&& x vs thing& x?
I think the explanation of std::forward as static_cast<T&&> is confusing. Our intuition for a cast is that it converts a type to some other type -- in this case it would be a conversion to an rvalue reference. It's not! So we are explaining one mysterious thing using another mysterious thing. This particular cast is defined by a table in Xeo's answer. But the question is: Why? So here's my understanding:
Suppose I want to pass you an std::vector<T> v that you're supposed to store in your data structure as data member _v. The naive (and safe) solution would be to always copy the vector into its final destination. So if you are doing this through an intermediary function (method), that function should be declared as taking a reference. (If you declare it as taking a vector by value, you'll be performing an additional totally unnecessary copy.)
void set(const std::vector<T> & v) { _v = v; }
This is all fine if you have an lvalue in your hand, but what about an rvalue? Suppose that the vector is the result of calling a function makeAndFillVector(). If you performed a direct assignment:
_v = makeAndFillVector();
the compiler would move the vector rather than copy it. But if you introduce an intermediary, set(), the information about the rvalue nature of your argument would be lost and a copy would be made.
set(makeAndFillVector()); // set will still make a copy
In order to avoid this copy, you need "perfect forwarding", which would result in optimal code every time. If you're given an lvalue, you want your function to treat it as an lvalue and make a copy. If you're given an rvalue, you want your function to treat it as an rvalue and move it.
Normally you would do it by overloading the function set() separately for lvalues and rvalues:
set(const std::vector<T> & lv) { _v = v; }
set(std::vector<T> && rv) { _v = std::move(rv); }
But now imagine that you're writing a template function that accepts T and calls set() with that T (don't worry about the fact that our set() is only defined for vectors). The trick is that you want this template to call the first version of set() when the template function is instantiated with an lvalue, and the second when it's initialized with an rvalue.
First of all, what should the signature of this function be? The answer is this:
template<class T>
void perfectSet(T && t);
Depending on how you call this template function, the type T will be somewhat magically deduced differently. If you call it with an lvalue:
std::vector<T> v;
perfectSet(v);
the vector v will be passed by reference. But if you call it with an rvalue:
perfectSet(makeAndFillVector());
the (anonymous) vector will be passed by rvalue reference. So the C++11 magic is purposefully set up in such a way as to preserve the rvalue nature of arguments if possible.
Now, inside perfectSet, you want to perfectly pass the argument to the correct overload of set(). This is where std::forward is necessary:
template<class T>
void perfectSet(T && t) {
set(std::forward<T>(t));
}
Without std::forward the compiler would have to assume that we want to pass t by reference. To convince yourself that this is true, compare this code:
void perfectSet(T && t) {
set(t);
set(t); // t still unchanged
}
to this:
void perfectSet(T && t) {
set(std::forward<T>(t));
set(t); // t is now empty
}
If you don't explicitly forward t, the compiler has to defensively assume that you might be accessing t again and chose the lvalue reference version of set. But if you forward t, the compiler will preserve the rvalue-ness of it and the rvalue reference version of set() will be called. This version moves the contents of t, which means that the original becomes empty.
This answer turned out much longer than what I initially assumed ;-)
First, let's take a look at what std::forward does according to the standard:
§20.2.3 [forward] p2
Returns: static_cast<T&&>(t)
(Where T is the explicitly specified template parameter and t is the passed argument.)
Now remember the reference collapsing rules:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
(Shamelessly stolen from this answer.)
And then let's take a look at a class that wants to employ perfect forwarding:
template<class T>
struct some_struct{
T _v;
template<class U>
some_struct(U&& v)
: _v(static_cast<U&&>(v)) {} // perfect forwarding here
// std::forward is just syntactic sugar for this
};
And now an example invocation:
int main(){
some_struct<int> s1(5);
// in ctor: '5' is rvalue (int&&), so 'U' is deduced as 'int', giving 'int&&'
// ctor after deduction: 'some_struct(int&& v)' ('U' == 'int')
// with rvalue reference 'v' bound to rvalue '5'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int&&>(v)'
// this just turns 'v' back into an rvalue
// (named rvalue references, 'v' in this case, are lvalues)
// huzzah, we forwarded an rvalue to the constructor of '_v'!
// attention, real magic happens here
int i = 5;
some_struct<int> s2(i);
// in ctor: 'i' is an lvalue ('int&'), so 'U' is deduced as 'int&', giving 'int& &&'
// applying the reference collapsing rules yields 'int&' (& + && -> &)
// ctor after deduction and collapsing: 'some_struct(int& v)' ('U' == 'int&')
// with lvalue reference 'v' bound to lvalue 'i'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int& &&>(v)'
// after collapsing rules: 'static_cast<int&>(v)'
// this is a no-op, 'v' is already 'int&'
// huzzah, we forwarded an lvalue to the constructor of '_v'!
}
I hope this step-by-step answer helps you and others understand just how std::forward works.
It works because when perfect forwarding is invoked, the type T is not the value type, it may also be a reference type.
For example:
template<typename T> void f(T&&);
int main() {
std::string s;
f(s); // T is std::string&
const std::string s2;
f(s2); // T is a const std::string&
}
As such, forward can simply look at the explicit type T to see what you really passed it. Of course, the exact implementation of doing this is non-trival, if I recall, but that's where the information is.
When you refer to a named rvalue reference, then that is indeed an lvalue. However, forward detects through the means above that it is actually an rvalue, and correctly returns an rvalue to be forwarded.

Why is std::is_const::value 'false' even though T's value_type is const?

#include <type_traits>
struct foo;
int main()
{
const foo *bar;
static_assert(std::is_const<decltype(*bar)>::value,
"expected const but this is non-const!");
}
This results in a failing static_assert which is unexpected. This is somewhat similar to this question on const references but not quite the same.
In my case, dereferencing bar should give an instance of const foo as its type but yet std::is_const is saying otherwise.
Shortly that's because a reference or a pointer to a const type is not a const type.
Note that decltype(*bar) isn't const foo, it's const foo & and they are really different beasts.
Consider the example given here:
std::cout << std::is_const<const int *>::value << '\n'; // false
std::cout << std::is_const<int * const>::value << '\n'; // true
We see that std::is_const<const int *>::value is false and std::is_const<int * const>::value is true.
That's because in const int * the type is pointer to something const, that is not a const type as intended by is_const (and the standard actually). In int * const the const qualifier applies to the pointer type and not to the pointed one, thus the type is a const one, no matter to what it points.
Something similar applies for const foo &, that is a reference to something const.
You can solve using this instead:
static_assert(std::is_const<std::remove_reference_t<decltype(*bar)>>::value, "expected const but this is non-const!");
Or even this, for you don't need to do *bar actually:
static_assert(std::is_const<std::remove_pointer_t<decltype(bar)>>::value, "expected const but this is non-const!");
In this case, by removing the pointer/reference with remove_pointer_t/remove_reference_t your type becomes const foo, that is actually a const type.
As a side note, the example above uses the C++14-ish std::remove_reference_t and std::remove_pointer_t type traits.
You can easily turn those lines of code to C++11 as it follows:
static_assert(std::is_const<typename std::remove_pointer<decltype(bar)>:: type>::value, "expected const but this is non-const!");
It's worth mentioning a few comments to the answer to give more details:
Thanks to #DanielFischer for the question:
Is there a short explanation why decltype(*bar) is const foo& rather than const foo?
I'm not a language-lawyer, but I guess it can be deduced from [expr.unary.op]/1 (emphasis mine):
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
And [dcl.type.simple]/4.4 (emphasis mine):
otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
Both referring to the working draft.
Thanks to #LightnessRacesInOrbit for the comment. Note that decltype(*bar) being const foo & is a funny C++ quirk of decltype, since *bar is not const foo &.

Why rvalue reference argument matches to const reference in overload resolution?

Potentially related articles:
Overload resolution between object, rvalue reference, const reference
std::begin and R-values
For a STL container C, std::begin(C) and similar access functions including std::data(C) (since C++17) are supposed to have the same behavior of C::begin() and the other corresponding C's methods. However, I am observing some interesting behaviors due to the details of overload resolution involving lvalue/rvalue references and constness.
DataType1 is int* as easily expected. Also, confirmed the by with Boost's type_id_with_cvr. const vector<int> gives int const* No surprise here.
using T = vector<int>;
using DataType1 = decltype(T().data()); // int*
using CT = const T;
using DataType2 = decltype(CT().data()); // int const*
using boost::typeindex::type_id_with_cvr;
cout << type_id_with_cvr<DataType1>() << endl; // prints int*
...
I tried std::data, which can also handle arrays and non-STL containers. But it yields int const*. Similarly, std::begin returns a const iterator, even though T is not const.
using T = vector<int>;
using DataType3 = decltype(std::data(T())); // int const* Why ???
using CT = const T;
using DataType4 = decltype(std::data(CT())); // int const*
Question: What is the reason of this difference? I expected that C.data() and std::data(C) would behave in the same manner.
Some my research: In order to get int* for DataType3, T must be converted to non-const lvalue reference type explicitly. I tried declval, and it was working.
using DataType3 = decltype(std::data(std::declval<T&>())); // int*
std::data provides two overloads:
template <class _Cont> constexpr
auto data(_Cont& __c) -> decltype(__c.data()) { return __c.data(); }
// non-const rvalue reference argument matches to this version.
template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }
While resolving overloaded functions for std::data, T(), which is non-const rvalue reference, is matched to the const T& version instead of T& version.
It was not easy to find this specific overload resolution rule in the standard (13.3, over.match). It'd be much clearer if someone could point the exact rules for this issue.
This behaviour is attributed to overload resolution rules. As per standard 8.5.3/p5.2 References [dcl.init.ref], rvalue references bind to const lvalue references. In this example:
std::data(T())
You provide to std::data an rvalue. Thus, due to overload resolution rules the overload:
template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }
is a better match. Consequently you get const int*
You can't bind a temporary to a non-const lvalue reference.
The only line that is mildly surprising is using DataType1 = decltype(T().data()); // int*.
...but it is still normal, member functions can be called on temporary objects without being treated as const. This is another non trivial difference between member functions and free functions.
For example, in C++98 (pre-rvalue refs) it was not possible to do std::ofstream("file.txt") << std::string("text") because operator<< is not member and the temporary is treated as const. If operator<< were a member of std::ofstream it would be possible (and may even make sense). (The situation changed later in C++11 with rvalue references but the point is still valid).
Here it is an example:
#include<iostream>
struct A{
void f() const{std::cout << "const member" << std::endl;}
void f(){std::cout << "non const member" << std::endl;}
};
void f(A const&){std::cout << "const function" << std::endl;}
void f(A&){std::cout << "non const function" << std::endl;}
int main(){
A().f(); // prints "non const member"
f(A()); // prints "const function"
}
The behavior is exposed when the object is temporarily constructed and used. In all other cases I can imagine, member f is equivalent to f free function.
(r-value reference qualifications && --for member and function-- can give you a more fine grained control but it was not part of the question.)

How does std::forward work? [duplicate]

This question already has answers here:
Closed 11 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
Possible Duplicate:
Advantages of using forward
I know what it does and when to use it but I still can't wrap my head around how it works. Please be as detailed as possible and explain when std::forward would be incorrect if it was allowed to use template argument deduction.
Part of my confusion is this:
"If it has a name, it's an lvalue" - if that's the case why does std::forward behave differently when I pass thing&& x vs thing& x?
I think the explanation of std::forward as static_cast<T&&> is confusing. Our intuition for a cast is that it converts a type to some other type -- in this case it would be a conversion to an rvalue reference. It's not! So we are explaining one mysterious thing using another mysterious thing. This particular cast is defined by a table in Xeo's answer. But the question is: Why? So here's my understanding:
Suppose I want to pass you an std::vector<T> v that you're supposed to store in your data structure as data member _v. The naive (and safe) solution would be to always copy the vector into its final destination. So if you are doing this through an intermediary function (method), that function should be declared as taking a reference. (If you declare it as taking a vector by value, you'll be performing an additional totally unnecessary copy.)
void set(const std::vector<T> & v) { _v = v; }
This is all fine if you have an lvalue in your hand, but what about an rvalue? Suppose that the vector is the result of calling a function makeAndFillVector(). If you performed a direct assignment:
_v = makeAndFillVector();
the compiler would move the vector rather than copy it. But if you introduce an intermediary, set(), the information about the rvalue nature of your argument would be lost and a copy would be made.
set(makeAndFillVector()); // set will still make a copy
In order to avoid this copy, you need "perfect forwarding", which would result in optimal code every time. If you're given an lvalue, you want your function to treat it as an lvalue and make a copy. If you're given an rvalue, you want your function to treat it as an rvalue and move it.
Normally you would do it by overloading the function set() separately for lvalues and rvalues:
set(const std::vector<T> & lv) { _v = v; }
set(std::vector<T> && rv) { _v = std::move(rv); }
But now imagine that you're writing a template function that accepts T and calls set() with that T (don't worry about the fact that our set() is only defined for vectors). The trick is that you want this template to call the first version of set() when the template function is instantiated with an lvalue, and the second when it's initialized with an rvalue.
First of all, what should the signature of this function be? The answer is this:
template<class T>
void perfectSet(T && t);
Depending on how you call this template function, the type T will be somewhat magically deduced differently. If you call it with an lvalue:
std::vector<T> v;
perfectSet(v);
the vector v will be passed by reference. But if you call it with an rvalue:
perfectSet(makeAndFillVector());
the (anonymous) vector will be passed by rvalue reference. So the C++11 magic is purposefully set up in such a way as to preserve the rvalue nature of arguments if possible.
Now, inside perfectSet, you want to perfectly pass the argument to the correct overload of set(). This is where std::forward is necessary:
template<class T>
void perfectSet(T && t) {
set(std::forward<T>(t));
}
Without std::forward the compiler would have to assume that we want to pass t by reference. To convince yourself that this is true, compare this code:
void perfectSet(T && t) {
set(t);
set(t); // t still unchanged
}
to this:
void perfectSet(T && t) {
set(std::forward<T>(t));
set(t); // t is now empty
}
If you don't explicitly forward t, the compiler has to defensively assume that you might be accessing t again and chose the lvalue reference version of set. But if you forward t, the compiler will preserve the rvalue-ness of it and the rvalue reference version of set() will be called. This version moves the contents of t, which means that the original becomes empty.
This answer turned out much longer than what I initially assumed ;-)
First, let's take a look at what std::forward does according to the standard:
§20.2.3 [forward] p2
Returns: static_cast<T&&>(t)
(Where T is the explicitly specified template parameter and t is the passed argument.)
Now remember the reference collapsing rules:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
(Shamelessly stolen from this answer.)
And then let's take a look at a class that wants to employ perfect forwarding:
template<class T>
struct some_struct{
T _v;
template<class U>
some_struct(U&& v)
: _v(static_cast<U&&>(v)) {} // perfect forwarding here
// std::forward is just syntactic sugar for this
};
And now an example invocation:
int main(){
some_struct<int> s1(5);
// in ctor: '5' is rvalue (int&&), so 'U' is deduced as 'int', giving 'int&&'
// ctor after deduction: 'some_struct(int&& v)' ('U' == 'int')
// with rvalue reference 'v' bound to rvalue '5'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int&&>(v)'
// this just turns 'v' back into an rvalue
// (named rvalue references, 'v' in this case, are lvalues)
// huzzah, we forwarded an rvalue to the constructor of '_v'!
// attention, real magic happens here
int i = 5;
some_struct<int> s2(i);
// in ctor: 'i' is an lvalue ('int&'), so 'U' is deduced as 'int&', giving 'int& &&'
// applying the reference collapsing rules yields 'int&' (& + && -> &)
// ctor after deduction and collapsing: 'some_struct(int& v)' ('U' == 'int&')
// with lvalue reference 'v' bound to lvalue 'i'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int& &&>(v)'
// after collapsing rules: 'static_cast<int&>(v)'
// this is a no-op, 'v' is already 'int&'
// huzzah, we forwarded an lvalue to the constructor of '_v'!
}
I hope this step-by-step answer helps you and others understand just how std::forward works.
It works because when perfect forwarding is invoked, the type T is not the value type, it may also be a reference type.
For example:
template<typename T> void f(T&&);
int main() {
std::string s;
f(s); // T is std::string&
const std::string s2;
f(s2); // T is a const std::string&
}
As such, forward can simply look at the explicit type T to see what you really passed it. Of course, the exact implementation of doing this is non-trival, if I recall, but that's where the information is.
When you refer to a named rvalue reference, then that is indeed an lvalue. However, forward detects through the means above that it is actually an rvalue, and correctly returns an rvalue to be forwarded.

Argument type deduction, references and rvalues

Consider the situation where a function template needs to forward an argument while keeping it's lvalue-ness in case it's a non-const lvalue, but is itself agnostic to what the argument actually is, as in:
template <typename T>
void target(T&) {
cout << "non-const lvalue";
}
template <typename T>
void target(const T&) {
cout << "const lvalue or rvalue";
}
template <typename T>
void forward(T& x) {
target(x);
}
When x is an rvalue, instead of T being deduced to a constant type, it gives an error:
int x = 0;
const int y = 0;
forward(x); // T = int
forward(y); // T = const int
forward(0); // Hopefully, T = const int, but actually an error
forward<const int>(0); // Works, T = const int
It seems that for forward to handle rvalues (without calling for explicit template arguments) there needs to be an forward(const T&) overload, even though it's body would be an exact duplicate.
Is there any way to avoid this duplication?
This is a known problem and the purpose of rvalue references in C++0x. The problem has no generic solution in C++03.
There is some archaic historical reason why this occurs that is very pointless. I remember asking once and the answer depressed me greatly.
In general, this nasty problem with templates ought to require duplication because the semantics where a variable is const or not, or a reference or not, are quite distinct.
The C++11 solution to this is "decltype" but it is a bad idea because all it does is compound and already broken type system.
No matter what the Standard or the Committee says, "const int" is not and will never be a type. Nor will "int&" ever be a type. Therefore a type parameter in a template should never be allowed to bind to such non-types, and thankfully for deduction this is the case. Unfortunately you can still explicitly force this unprincipled substitution.
There are some idiotic rules which try to "fix" this problem, such as "const const int" reducing to "const int", I'm not even sure what happens if you get "int & &": remember even the Standard doesn't count "int&" as a type, there is a "int lvalue" type but that's distinct:
int x; // type is lvalue int
int &y = x; // type is lvalue int
The right solution to this problem is actually quite simple: everything is a mutable object. Throw out "const" (it isn't that useful) and throw away references, lvalues and rvalues. It's quite clear that all class types are addressable, rvalue or not (the "this" pointer is the address). There was a vain attempt by the committee to prohibit assigning to and addressing rvalues.. the addressing case works but is easily escaped with a trivial cast. The assignment case doesn't work at all (because assignment is a member function and rvalues are non-const, you can always assign to a class typed rvalue).
Anyhow the template meta-programming crowd have "decltype" and with that you can find the encoding of a declaration including any "const" and "&" bits and then you can decompose that encoding using various library operators. This couldn't be done before because that information is not actually type information ("ref" is actually storage allocation information).
When x is an rvalue
But x is never an rvalue, because names are lvalues.
Is there any way to avoid this duplication?
There is a way in C++0x:
#include <utility>
template <typename T>
void forward(T&& x) // note the two ampersands
{
target(std::forward<T>(x));
}
Thanks to reference collapsing rules, the expression std::forward<T>(x) is of the same value-category as the argument to your own forward function.
Assuming there are k arguments, as I understand it the only "solution" in C++03 is to manually write out 2^k forwarding functions taking every possible combination of & and const& parameters. For illustration, imagine that target() actually took 2 parameters. You would then need:
template <typename T>
void forward2(T& x, T& y) {
target(x, y);
}
template <typename T>
void forward2(T& x, T const& y) {
target(x, y);
}
template <typename T>
void forward2(T const& x, T& y) {
target(x, y);
}
template <typename T>
void forward2(T const& x, T const& y) {
target(x, y);
}
Obviously this gets very unwieldy for large k, hence rvalue references in C++0x as other answers have mentioned.