#include <iostream>
#include <type_traits>
using namespace std;
template<typename T>
void f(T&&)
{
cout << boolalpha << std::is_const_v<T> << endl;
cout << boolalpha << std::is_const_v<T&&> << endl;
}
int main()
{
const int n = 1;
f(n);
}
The output is:
false
false
Here, n is an obvious const variable, why does std::is_const_v not behave as expected?
std::is_const is false when the type is a reference:
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>
In your specific case, T is a forwarding reference, which will be deduced as lvalue reference when passing a lvalue argument. That's why you see the false in two cases.
Related
In C++, it is possible to use std::is_same to check if two types are exactly identical. Is there a way to check if two types are identical except, maybe, a const or a & modifier? Here is an example:
#include <type_traits>
#include <iostream>
using namespace std;
int main() {
cout << boolalpha;
cout << is_same<char,int>::value << endl; // false - OK
cout << is_same<char,char>::value << endl; // true - OK
cout << is_same<char,const char>::value << endl; // false - should be true
cout << is_same<char,const char&>::value << endl; // false - should be true
}
Removing cv-qualifiers as well as returning a non reference type will be supported from C++20 onward
std::remove_cvref
However as of current standard, you can use Type modifications functions in conjunction
template<class T1, class T2>
void print_is_same() {
std::cout << std::is_same<T1, T2>() << '\n';
}
int main() {
std::cout << std::boolalpha;
print_is_same<char, int>(); //false
print_is_same<char, char>(); //true
print_is_same<char, std::remove_const<const char>::type>(); //true
print_is_same<char, std::remove_const<std::remove_reference<const char &>::type>::type>(); //true
}
Or probably create a type alias such as
template<typename T>
using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
I found an alternative solution: instead of removing const and &, we can add them:
template<class T1, class T2>
bool is_almost_same_v = std::is_same_v<const T1&,const T2&>;
Indeed:
cout << is_almost_same_v<char,int> << endl; // false
cout << is_almost_same_v<char,char> << endl; // true
cout << is_almost_same_v<char,const char> << endl; // true
cout << is_almost_same_v<char,const char&> << endl; // true
In many cases, std::is_same_v<std::decay_t<TYPE_TO_TEST>, TYPE_TO_COMPARE> will do the trick. std::decay_t<TYPE> also converts array types to pointers to the array members, so std::is_same_v<std::decay_t<int[5]>, int*> also yields true. But in many cases, this behavior is even beneficial, especially, if your testing requirement is "can be used like a pointer".
I was writing a test for my iterator types and wanted to check that the reference returned by de-referencing iterators provided by begin() and cbegin() are non-const and const respectively.
I tried doing something similar to the following : -
#include <type_traits>
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec{0};
std::cout << std::is_const<decltype(*vec.begin())>::value << std::endl;
std::cout << std::is_const<decltype(*vec.cbegin())>::value << std::endl;
}
But this prints 0 for both cases.
Is there a way to check if a reference is const?
I can use C++11/14/17 features.
Remove the reference to get the referenced type to inspect its constness. A reference itself is never const - even though references to const may colloquially be called const references:
std::is_const_v<std::remove_reference_t<decltype(*it)>>
*it will be a reference rather than the referenced type (int& or const int& rather than int or const int in your case). So, you need to remove the reference:
#include <iostream>
#include <type_traits>
#include <vector>
int main() {
std::vector<int> vec{0};
std::cout << std::is_const<std::remove_reference<decltype(*vec.begin())>::type>::value << std::endl;
std::cout << std::is_const<std::remove_reference<decltype(*vec.cbegin())>::type>::value << std::endl;
}
This produces:
0
1
Note: The above works uses C++11. #eerorika's answer is more terse, but requires C++17.
is_const always returns false for references. Instead, do:
std::is_const_v<std::remove_reference_t<decltype(*v.begin() )>> // false
std::is_const_v<std::remove_reference_t<decltype(*v.cbegin())>> // true
You can check the notes on document here:
https://en.cppreference.com/w/cpp/types/is_const
Notes
If T is a reference type then is_const::value is always false. The
proper way to check a potentially-reference type for const-ness is to
remove the reference: is_const::type>.
for(auto it=vec.begin(); it!=vec.end(); ++it) {
std::cout << std::is_const<std::remove_reference<decltype(*it)>::type>::value << std::endl;
}
for(auto it=vec.cbegin(); it!=vec.cend(); ++it) {
std::cout << std::is_const<std::remove_reference<decltype(*it)>::type>::value << std::endl;
}
I have read many posts about variadic templates and std::bind but I think I am still not understanding how they work together. I think my concepts are a little hazy when it comes to using variadic templates, what std::bind is used for and how they all tie together.
In the following code my lambda uses the dot operator with objects of type TestClass but even when I pass in objects of type std::ref they still work. How is this exactly? How does the implicit conversion happen?
#include <iostream>
using std::cout;
using std::endl;
#include <functional>
#include <utility>
using std::forward;
class TestClass {
public:
TestClass(const TestClass& other) {
this->integer = other.integer;
cout << "Copy constructed" << endl;
}
TestClass() : integer(0) {
cout << "Default constructed" << endl;
}
TestClass(TestClass&& other) {
cout << "Move constructed" << endl;
this->integer = other.integer;
}
int integer;
};
template <typename FunctionType, typename ...Args>
void my_function(FunctionType function, Args&&... args) {
cout << "in function" << endl;
auto bound_function = std::bind(function, args...);
bound_function();
}
int main() {
auto my_lambda = [](const auto& one, const auto& two) {
cout << one.integer << two.integer << endl;
};
TestClass test1;
TestClass test2;
my_function(my_lambda, std::ref(test1), std::ref(test2));
return 0;
}
More specifically, I pass in two instances of a reference_wrapper with the two TestClass objects test1 and test2, but when I pass them to the lambda the . operator works magically. I would expect that you have use the ::get() function in the reference_wrapper to make this work but the call to the .integer data member works..
The reference unwrapping is performed by the result of std::bind():
If the argument is of type std::reference_wrapper<T> (for example, std::ref or std::cref was used in the initial call to bind), then the reference T& stored in the bound argument is passed to the invocable object.
Corresponding standardese can be found in N4140 draft, [func.bind.bind]/10.
It is important to note that with std::bind;
The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref.
The "passed by reference" above is achieved because std::ref provides a result of std::reference_wrapper that is a value type that "wraps" the reference provided.
std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.
By way of an example of what bind's unwrapping of the reference does (without the bind);
#include <iostream>
#include <utility>
#include <functional>
int main()
{
using namespace std;
int a = 1;
auto b = std::ref(a);
int& c = b;
cout << a << " " << b << " " << c << " " << endl; // prints 1 1 1
c = 2;
cout << a << " " << b << " " << c << " " << endl; // prints 2 2 2
}
Demo code.
This is a spin off of the question How to check if object is const or not?.
I was surprised to see the following program
#include <iostream>
#include <type_traits>
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_const<const int&>::value << "\n";
}
produced this output
false
In what context does it make sense to think of const int& as a non-const type?
Perhaps it'll be easier to understand with this example
std::cout << std::is_const<int const *>::value << "\n"; // pointer to const int
std::cout << std::is_const<int * const>::value << "\n"; // const pointer to int
Output:
false
true
The first type is a pointer to a const int, while in the second the int * itself is const. Hence it results in true while the former is false. Similarly, what you have a reference to a const int. If int& const were valid it'd result in true.
A const qualifier on a reference just means that the value can't be modified via the reference. It can still be modified by other means. For example:
int a = 1;
const int &b = a;
std::cout << b << std::endl; // Prints 1
a = 2;
std::cout << b << std::endl; // Prints 2
Thus, you can't assume that the value of a const reference is actually constant.
It appears that to test for const-ness, one must test the template-parameter, but to test for rvalue-ness, one must test an actual parameter. (This is using VC++ 2012.) This code illustrates what I mean:
#include <type_traits>
#include <string>
#include <iostream>
using namespace std;
template<class T>
void f(T& x) {
cout << "f() is_const<T> and is_const<decltype<x)>" << endl;
cout << is_const<T>::value << endl; // Prints 1 when arg is const
cout << is_const<decltype(x)>::value << endl; // Prints 0 when arg is const
}
template<class T>
void g(T&& x) {
cout << "g() is_const<T> and is_const<decltype<x)>" << endl;
cout << is_const<T>::value << endl; // Prints 0 when arg is const
cout << is_const<decltype(x)>::value << endl; // Prints 0 when arg is cons
cout << "g() is_rvalue_reference<T> and is_rvalue_reverence<decltype(x)>" <<endl;
cout << is_rvalue_reference<T>::value << endl; // Prints 0 when arg is rvlaue
cout << is_rvalue_reference<decltype(x)>::value << endl; // Prints 1 when arg is rvalue
}
int main()
{
const std::string str;
f(str); // const argument
cout << endl;
g(std::string("")); // rvalue argument
return 0;
}
I am struggling to understand why that is. Can someone explain, or point me to an article that explains it? If need be, I will dig into the C++11 standard. Anyone know the pertinent sections?
The reason is that you're misunderstanding things. x will never be const in any of those examples, simply because there are no const reference types (you can't change what a reference refers to anyways). In is_const<T> you're basically ignoring that you declared x as T&.
A similar misunderstanding is at work for the rvalue ref test. The T in T&& (which is called a universal reference, btw) will be deduced as U& when you pass an lvalue and as U when you pass an rvalue. When testing is_rvalue_reference<T>, you're ignoring again that you declared x as T&&. When testing is_const<T>, you didn't account for the fact that T will be a reference, which, as said above, can never be const.
The correct tests for g would be
std::is_const<typename std::remove_reference<T>::type>::value and
std::is_rvalue_reference<T&&>::value