Clarification wanted re. C++ type_traits - c++

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

Related

Types should be the same but are not

I am not sure I understand why the first test evaluates to true and the second to false. I know that the information from typeid().name() is usually not reliable, but my main problem is with the typeid itself. I don't understand why the type of *test is not Location<1>, or what else is wrong. Any thoughts? Is there same wrapper around a type here that I don't see? Thanks in advance, and apologies if the answer is obvious.
#include <iostream>
#include <utility>
#include <typeinfo>
class LocationAbstract
{
virtual void get_() = 0;
};
template<int i>
class Location : public LocationAbstract
{
public:
static constexpr int test = i;
virtual void get_() override
{
return;
}
};
template <int i>
Location<i> LocationGenerator()
{
Location<i> test{};
return test;
}
int main()
{
LocationAbstract *table[10];
table[0] = new decltype(LocationGenerator<0>());
table[1] = new decltype(LocationGenerator<1>());
Location<1> *test;
try
{
std::cout << "Casting\n";
test = dynamic_cast<Location<1>*>(table[1]);
}
catch (std::bad_cast &e)
{
std::cout << "Bad cast\n";
}
// test1, evaluates to true
std::cout << (typeid(*test) == typeid(*dynamic_cast<Location<1>*>(table[1]))) << "\n";
std::cout << typeid(*test).name() << "\n";
std::cout << typeid(*dynamic_cast<Location<1>*>(table[1])).name() << "\n----\n";
// test2, why does this evaluate to false while the above evaluates to true ?
std::cout << (typeid(Location<1>()) == typeid(*dynamic_cast<Location<1>*>(table[1]))) << "\n";
std::cout << typeid((Location<1>())).name() << "\n";
std::cout << typeid(*dynamic_cast<Location<1>*>(table[1])).name() << "\n";
auto test1 = Location<1>();
auto test2 = *dynamic_cast<Location<1>*>(table[1]);
std::cout << typeid(test1).name() << " and " << typeid(test2).name() << "\n";
return 0;
}
An extra set of () makes all the difference here. In typeid(Location<1>()) and typeid((Location<1>())), Location<1>() actually means two totally different things.
In typeid(Location<1>()), Location<1>() is interpreted as a function type that returns a Location<1> and takes no parameters.
In typeid((Location<1>())), Location<1>() is interpreted as value-initializing an anonymous Location<1> object.
The typeid operator can work on either types or expressions. That is, you can say typeid(int) as well as typeid(42). Since Location<1>() can be interpreted as a type, the language does so. (Location<1>()) cannot be interpreted as a type though, so it must be interpreted as an expression. The only thing Location<1>() can mean as part of an expression is to value-initialize an anonymous Location<1> object, so typeid gives you the type of that object.
Let this be yet another reason to prefer uniform-initialization syntax when creating temporary objects; Location<1>{} would not have this ambiguity.
Examine these two lines:
std::cout << (typeid(Location<1>()) == typeid(*dynamic_cast<Location<1>*>(table[1]))) << "\n";
std::cout << typeid((Location<1>())).name() << "\n";
In the first line, you use typeid(Location<1>()). typeid can take types as well as expressions, and Location<1>() is a function type with no parameters and a return type of Location<1>.
So why does the name print the same? That's because of the second line: typeid((Location<1>())). By wrapping the argument in parentheses, it is no longer a valid type, so it is treated as an expression and the name of typeid(Location<1>) is printed. Removing the extra parentheses prints F8LocationILi1EEvE under the same mangling scheme.
To avoid the ambiguity, you can also use the type directly (typeid(Location<1>)) or use braces: typeid(Location<1>{})).

How does c++ choose move and copy construct

I'm working out an "Any" class by myself. As following code shown, I have two questions.
#include <assert.h>
#include <iostream>
#include <typeinfo>
class Test{};
class Any {
public:
template<typename DataType>
explicit Any(DataType&& in) {
Test t;
std::cout
<< typeid(t).name() << " "
<< typeid(in).name() << " "
<< typeid(Test()).name();
std::cout << " move";
}
template<typename DataType>
explicit Any(const DataType& in) {
Test t;
std::cout
<< typeid(t).name() << " "
<< typeid(in).name() << " "
<< typeid(Test()).name();
std::cout << " copy";
}
};
int main()
{
Test t;
Any a(t);
}
Compilation command is
g++ main.cpp -std=c++11
The output is
4Test 4Test F4TestvE move
Why does c++ choose move construct rather than copy construct? "t" is an instance of Test which it's not a rvalue.
Why typeid(in) and typeid(Test()) is not same? They are both rvalue.
Thanks a lot.
Why does c++ choose move construct rather than copy construct? "t" is an instance of Test which it's not a rvalue.
The 1st constructor overload takes forwarding reference and could accept both lvalues and rvalues. (Hence it's not move constructor.) For Any a(t); it's an exact match, while for the 2nd overload t needs to be converted to const.
Why typeid(in) and typeid(Test()) is not same? They are both rvalue.
Test() is a function type, which returns Test and takes nothing, thus typeid(Test()) gives different result.
The reason your move variant is invoked is because its signature doesn't actually require an rvalue reference to be passed.
If U = T const& then U&& will be a T const&, not a T&&. In your case DataType binds to Test&, not, as you likely expected, to Test. The && therefore collapses and does nothing. You can see this in action if you add a static_assert(std::is_same_v<DataType, Test&>); which will pass.
You function will be called if you replace the template with a fixed Type, i.e. Test&& instead of DataType&&.
Have a look at reference, section Reference collapsing.

Why does `std::is_const_v` not behave as expected?

#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.

how to return a reference to a default value of a non-type template argument

I have done a lot of research on this but I wasn't able to find a design pattern addressing the problem. This is a minimal description of what I'm trying to perform.
#include <iostream>
using namespace std;
template <class T, T default_value=T{}>
class A{
private:
T inclassValue;
public:
A(T icv):inclassValue{icv}{}
const T& operator[](int k){
if(k==1) return inclassValue;
return default_value;
}
};
struct two_int{int x;int y;};
int main(){
A<int> a{4};
cout << "a[0]=" << a[0] << endl;
cout << "a[1]=" << a[1] << endl;
/*
A<two_int> b{{3,5}};
cout << "b[0]=" << b[0].x << "," << b[0].y << endl;
cout << "b[1]=" << b[1].x << "," << b[1].y << endl;
*/
return 0;
}
The code will compile, link and output as expected
a[0]=0
a[1]=4
The compiler complains though and issues a warning for the line of code where default_value is used
return default_value;//Returning reference to local temporary object
which makes some sense. Uncommenting the last part in main and compiling, the compiler issue this time an error while building the template
template <class T, const T default_value= T{}>//A non-type template parameter cannot have type 'two_int'
while what I ideally hope for is
b[0]=0,0
b[1]=3,5
I was able to come up with a solution by adding an extra helper class, that will provide the default_value of T (as a static member), to the template arguments. I'm not convinced by the robustness of my trick and I was wondering if there exists a design pattern addressing this. The warning for types and the error for non-types. Also, I shall add that my primary goal is to be able to provide default_value at will (6 for int for example instead of 0).
Thanks
Not exactly sure what you're looking for, but perhaps a static helper finishing for creating a static default T could be useful:
template <typename T>
static const T& default_value() {
static const T* t = new T{};
return *t;
}
Note this will construct T at most once even across threads (in c++11), but still never destruct T. Since it's static, it's likely the lack of destruction is acceptable, but this of course depends on T.
Here is one version that forwards arguments to the constructor of a default_value stored as constexpr. You are quite limited here as to what is valid to pass as arguments (not sure exactly how limited) so it will depend on your use-case.
#include <iostream>
using namespace std;
template <class T, auto... Args>
class A{
private:
T inclassValue;
constexpr static T default_val = T{Args...}; // Changed to curly brackets here
public:
constexpr A(T icv):inclassValue{icv}{}
const T& operator[](int k){
if(k==1) return inclassValue;
return default_val;
}
};
struct two_int{int x;int y;};
int main(){
A<int> a{4};
cout << "a[0]=" << a[0] << endl;
cout << "a[1]=" << a[1] << endl;
A<two_int> b{{3,5}};
cout << "b[0]=" << b[0].x << "," << b[0].y << endl;
cout << "b[1]=" << b[1].x << "," << b[1].y << endl;
return 0;
}

A little hazy about std::ref() and std::bind() with variadic templates

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.