Chapter 4.11.3 of the book C++ Primer says the following:
A named cast has the following form: cast-name<type>(expression);
where type is the target type of the conversion, and expression is the value to be cast. If type is a reference, then the result is an lvalue.
Can anyone give an example of this? Is conversion to a reference even possible?
Is conversion to a reference even possible?
Yes, depending on the object you begin with.
Most simple example is one lvalue reference converted to another. This can even be an added cv-qualifier (or removed, with const_cast), such as:
int const & foo(int & i) {
return static_cast<int const &>(i);
}
the derived-to-base casts and base-to-derived (dynamic) casts mentioned in the other answers are other such examples.
A more interesting example is the std::reference_wrapper, which is an object you can receive as an rvalue, and cast it to an lvalue reference of the contained type:
int & foo(std::reference_wrapper<int> i) {
return static_cast<int &>(i);
}
Note that the cast above happens implicitly (I could've used return i), but in some contexts (e.g. capturing variables with auto type) you might want to write the cast explicitly.
What is meant in C++ Primer is simply that the behavior of casts in these examples, and others, is basically what you would expect - the result of a cast to a reference type is an lvalue.
Here is a short example with a class hierarchy. In main you find code that constructs a Derived object on the heap and stores the pointer to it in p. If you want to work with pointers, you can just dynamic_cast<Derived*> that pointer. But sometimes like in function work you have a reference to the base class. If inside the function you know at some place that the object b is of type Derived (maybe you checked an enum member giving you the type or based on some other information you know that it must be some special type), then can directly cast from reference Base& to reference Derived&. In that special case of dynamic_cast it also makes a difference because casting a pointer will return nullptr if the cast is not possible, but casting a reference will throw an exception if the cast is not possible.
#include <memory>
struct Base {
virtual ~Base() = default;
};
struct Derived : Base {
void f() const noexcept {}
};
void work(const Base& b) noexcept
{
// Check what to do, then assume it must be a Derived, then work on it:
dynamic_cast<const Derived&>(b).f();
}
int main() {
std::unique_ptr<Base> p{ std::make_unique<Derived>() };
dynamic_cast<Derived*>(p.get())->f();
work(*p);
return 0;
}
Example when type is not a reference: If i is a variable of type int, then static_cast<long int>(i) returns a non-lvalue of type long int.
Example when type is a reference: static_cast<Base&>(objectOfDerivedType) returns an lvalue of type reference-to-Base.
Related
I was trying to post this code as an answer to this question, by making this pointer wrapper (replacing raw pointer). The idea is to delegate const to its pointee, so that the filter function can't modify the values.
#include <iostream>
#include <vector>
template <typename T>
class my_pointer
{
T *ptr_;
public:
my_pointer(T *ptr = nullptr) : ptr_(ptr) {}
operator T* &() { return ptr_; }
operator T const*() const { return ptr_; }
};
std::vector<my_pointer<int>> filter(std::vector<my_pointer<int>> const& vec)
{
//*vec.front() = 5; // this is supposed to be an error by requirement
return {};
}
int main()
{
std::vector<my_pointer<int>> vec = {new int(0)};
filter(vec);
delete vec.front(); // ambiguity with g++ and clang++
}
Visual C++ 12 and 14 compile this without an error, but GCC and Clang on Coliru claim that there's an ambiguity. I was expecting them to choose non-const std::vector::front overload and then my_pointer::operator T* &, but no. Why's that?
[expr.delete]/1:
The operand shall be of pointer to object type or of class type. If of
class type, the operand is contextually implicitly converted (Clause
[conv]) to a pointer to object type.
[conv]/5, emphasis mine:
Certain language constructs require conversion to a value having one
of a specified set of types appropriate to the construct. An
expression e of class type E appearing in such a context is said
to be contextually implicitly converted to a specified type T and
is well-formed if and only if e can be implicitly converted to a type
T that is determined as follows: E is searched for non-explicit
conversion functions whose return type is cv T or reference to cv T
such that T is allowed by the context. There shall be exactly
one such T.
In your code, there are two such Ts (int * and const int *). It is therefore ill-formed, before you even get to overload resolution.
Note that there's a change in this area between C++11 and C++14. C++11 [expr.delete]/1-2 says
The operand shall have a pointer to object type, or a class type
having a single non-explicit conversion function (12.3.2) to a pointer
to object type. [...]
If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, [...]
Which would, if read literally, permit your code and always call operator const int*() const, because int* & is a reference type, not a pointer to object type. In practice, implementations consider conversion functions to "reference to pointer to object" like operator int*&() as well, and then reject the code because it has more than one qualifying non-explicit conversion function.
The delete expression takes a cast expression as argument, which can be const or not.
vec.front() is not const, but it must first be converted to a pointer for delete. So both candidates const int* and int* are possible candidates; the compiler cannot choose which one you want.
The eaiest to do is to use a cast to resolve the choice. For example:
delete (int*)vec.front();
Remark: it works when you use a get() function instead of a conversion, because the rules are different. The choice of the overloaded function is based on the type of the parameters and the object and not on the return type. Here the non const is the best viable function as vec.front()is not const.
union A {
public:
B b;
inline operator B() const {
return b;
}
}
void doSomething(B& b){};
A a;
doSomething(a);
Getting "no matching function for call to" error using code similar to above. Can you not implicilty cast to reference like this?
Work around?
In the call doSomething(a), a is converted to an rvalue of type B using the conversion operator (which returns by value, hence the new temporary object).
Since rvalues can only be used as non-const reference or value parameters, you can't call doSomething with an rvalue.
Either declare doSomething to take its argument by const reference, by value, or by rvalue-reference. You could also make operator B return a reference to the b member if that's appropriate.
The cast-operator returns b by value, and using that result when calling the doSomething function would create a temporary value, and you simply can't have references to temporary values because of their temporary nature.
Either change doSomething to receive its argument by value, or by const reference (which can bind to a temporary value), or possibly a rvalue-reference (using the && syntax in the argument declaration), or of course make a cast-operator that returns a reference.
At the moment I have a class defined similar to this:
class dummy{
public:
dummy(void(&func)(int))
: member{func}{}
void(&member)(int);
};
but I want to have member defined as a const function reference. I'm not sure exactly how to write this or if it is even possible.
P.S. PLEASE don't recommend me std::function I'm not oblivious to it's existence and have no objection to it, I just want to know whether something like this is doable.
The syntax:
Syntactically, you can achieve this through a type alias (or typedef):
using function_t = void(int); // or typedef void (function_t)(int);
class dummy {
public:
const function_t& member;
dummy(const function_t& func)
: member{func}{}
};
The semantics:
However, the "correct syntax" doesn't buy you anything: dummy is the same as
class dummy {
public:
function_t& member;
dummy(function_t& func)
: member{func}{}
};
(which, in turn is the same as the OP's definition) because the const qualifiers are ignored as per C++11 8.3.5/6:
The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored. [ Note: a function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types. —end note ]
What about rvalues?
As far as I undestand (from a comment to user657267's answer) the motivation for taking the argument as reference to const was to enable passing rvalues (temporaries):
void f(int) { }
function_t make_function() { return f; }
dummy d1(f);
dummy d2(make_function());
However, this doesn't compile, not because of dummy, but because make_function returns a function which is forbiden by C++11 8.3.5/8
If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed.99 Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions.
A natural "solution" would be returning a reference:
function& make_function() { return f; }
or
function&& make_function() { return f; }
In both cases, the type of the expression make_function() is an lvalue (which defies the purpose of dummy using reference to const to enable passing rvalues) as per C++11 5.2.2/10
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
Actually, value category is not an issue here. With any legal declaration of make_function and any definition of dummy seem above the declarations of d1 and d2 work fine.
Nevertheless, the mentioned comment to user657267's answer talks about passing a lambda but a reference to function cannot bind to lambda expression because it has a different type:
dummy d3([](int){}); // Error!
The suggested solution
Instead of references use pointers:
using function_t = void(int); // or typedef void (function_t)(int);
class dummy {
public:
function_t* member;
dummy(function_t* func)
: member{func}{}
};
void f(int) { }
function_t& make_function() { return f; }
dummy d1(f); // OK
dummy d2(make_function()); // OK
dummy d3([](int){}); // OK
Final remarks:
Again, declaring const function_t* member and dummy(const function_t* func) doesn't buy you anything because, as per references, the const qualifiers are ignored.
The initialization of d1 and d2 work because functions are implicitly converted to pointer to functions (see C++ 4.3/1).
If a function argument is of function type, the compiler changes its type to a pointer to function. Hence, dummy's constructor can be declared as dummy(function_t func);.
The initialization of d3 works because captureless lambdas are implicitly converted to pointer to functions. It wouldn't work for lambdas with captures.
I was refreshing my memory on the various types of casting available and came across the following on cppreference.com (http://en.cppreference.com/w/cpp/language/dynamic_cast):
1) If the type of expression is the exactly new_type or a less cv-qualified version of new_type, the result is expression.
Referring to the structure
dynamic_cast < new_type > ( expression )
I interpreted that to mean that, for example, attempting a dynamic_cast of a non-const reference to a const reference of the same type would actually yield the non-const reference and therefore allow me to call its non-const members, which is the opposite of what I'd expect. I wrote the following noddy piece of code to check that out:
#include<iostream>
class Base
{
int value;
public:
Base():value(0){};
virtual void ShowVal() const
{
printf("Value is %d\n", value);
}
virtual void SetVal(int val)
{
value = val;
}
};
int main ()
{
Base b;
Base& rB = b;
b.ShowVal();
(dynamic_cast<const Base&>(rB)).SetVal(2); //fails where (dynamic_cast<Base&>(rB)).SetVal(2); is obviously fine.
b.ShowVal();
}
As I'd have expected anyway, this doesn't compile and I get the error
blah.cpp:28:3: error: member function 'SetVal' not viable: 'this' argument has type 'const Base', but function is not marked const
(dynamic_cast<const Base&>(rB)).SetVal(2);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I suppose the question I'm meandering towards is: Have I misunderstood the rule above? And if so, what is it actually saying and is there a similarly basic example that demonstrates it?
Thanks. (In the meantime I'm going to read it again, and if I realise I've misread it then I'll quickly delete this question and commence facepalming...)
It's not so obvious from the way that's written, but the conversion still takes place. It just means that the object you get from the conversion (the pointer or reference) is the same as the one denoted by expression. The cast expression is of const type though because that's what you converted it to.
Here's what it looks like in the C++11 standard:
If the type of v is the same as T, or it is the same as T except that the class object type in T is more cv-qualified than the class object type in v, the result is v (converted if necessary).
Note the "converted if necessary".
You might wonder why it even bothers saying that the result is expression? For most dynamic_casts, the result is not expression. Consider casting from a pointer to base class to a pointer to derived class. You don't get the same pointer out of it. Instead, you get a pointer to the derived subobject.
It says the type of expression can be less cv-qualified than new-type. The type of the result is still new-type (the value is expression). Because that's specified.
There is a forward C struct declared in an unmodifiable header. I would like to "virtually" add convenience member functions to it. Obviously my first choice would be to extend the struct and add the methods to the derived class. No-can-do, as the struct itself is declared as "forward" in the header, so I get the error "error: invalid use of incomplete type ...". I get a similar error if I try to define a new struct with a single element of the old struct. This sucks.
However, I was thinking that I could do some hackery with reinterpret_cast to get it to work anyway. The way it would go is this:
//defined in header
struct A forward;
void do_something_with_A(A* a, int arg);
//defined in my wrapper
struct B {
B* wrap(A* a) {return reinterpret_cast<B*>(a); }
void do_something(int arg) {do_something_with_A(reinterpret_cast<A*>(this),arg); }
}
If I added implicit conversions from type B to type A, I was thinking that this could work out almost as if B was a zero-data inheritor of A. However, this obviously brings up the question: is this undefined in C++? Normally I would think that accessing an element of an illegally casted struct would be undefined; that makes sense. However, I would think that reinterpret_casting from one type to another, passing that pointer around, and then casting back again, without doing anything illegal in between would be fine. I would also think that the way a compiler implements non-virtual struct members would be creating a function
B::do_something(B* b, int arg)
and calling that with the appropriate argument for B. This then reduces to the previous case, which by my dubious logic is okay. So I would think calling .do_something on a struct which is actually a reinterpret_cast A would be okay.
However, this says nothing for what the C++ standard actually says on the matter. Any help with that? Also, if someone has information on how well this will work practically, (i.e. "Every compiler ever made accepts this", or "this only works with a few compilers") that would also be helpful, but slightly less so.
I believe that if you cast A* to B* and then cast it back to A* again, then the standard says you are OK. Those would be reinterpret_casts though not static_casts.
But what exactly is wrong with the normal solution?
class B
{
private:
A* ptr;
public:
B(A* p) : ptr(p) {}
void do_something(int arg) { do_something_with_A(ptr,arg); }
};
Seems to be as efficient as your solution and less mucking about.
I don't believe this works if you're using static_cast because you cannot static_cast between two completely unrelated class types. To be specific, if you have a pointer of type A* and try to convert it to a pointer of type B*, the static_cast only succeeds if this declaration is valid:
B* ptr(myAPtr);
or if B is non-virtually derived from A (which it isn't). See the ISO spec §5.2.9 for details on the specifics of this. If we consider the above declaration, the only possible conversions that could be applied here in all of §4 are those in §4.10, and of those the only one that might be applicable is conversion from base to derived classes (§4.10/3), but this doesn't apply here because A and B aren't related types.
The only cast you might be able to use here is a reinterpret_cast, and it doesn't look like this will work either. In particular, the behavior of casting across class hierarchies is (§5.2.10/7)
A pointer to an object can be explicitly converted to a pointer to an object of different type.65) Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
So immediately there's no guarantee that anything is going to work if the two objects have different alignment restrictions, and you cannot ensure that this is true. But suppose that you could. In that case, though, I believe that this will actually work correctly! Here's the reasoning. When you call the member function of the B object, then rule &5.2.2/1) kicks in and says that, since the function is nonvirtual:
[...] The function called in a member function call is normally selected according to the static type of the object expression. [...]
Okay, so we're at least calling the right function. Now, what about the this pointer? Well, according to &5.2.2/4:
[...] If the function is a nonstatic member function, the “this” parameter of the function (9.3.2) shall be initialized with a pointer to the object of the call, converted as if by an explicit type conversion (5.4). [...]
The type conversion done in the last part is the identity conversion from a B* to a B*, since that's the selected type. So you've called the right function with the this pointer set appropriately. Nice! Finally, when you do a reinterpret_cast back to the original type, by the earlier rule, you'll get back the A* object and everything will go as expected.
Of course, this only works if the objects have the same alignment requirements, and this cannot be guaranteed. Consequently, you shouldn't do it!
Hope this helps!