This is a general question about symmetry between pointer and reference types in the C++ language.
Is this table of correspondence meaningful in C++ (C++11 and beyond)?
+-----------+----------------+
| Reference | Pointer |
|-----------|----------------|
| T& | T* const |
| T const& | T const* const |
| T&& | ???* |
+-----------+----------------+
and if some, what would correspond to ???* ?. (Any additional rows are missing?)
(T&& is for a concrete type T, not a deduced type. See #ComicSansMS answer for the trailing const.)
My guess it will correspond to something like std::move_iterator<T*>, but it is not a built-in language feature and it seems to create a bunch of other problems (like std::reference_wrapper does).
Is the language missing some kind of r-value pointer to be more symmetric? Or better said something that generates r-values on derreference (for example a moved object -- or a even a copy).
I know that r-value references correspond to thing that "do not have a name", however that doesn't seem to be constradictory if that samething is only obtained for a deference operation *.
NOTE 1:
I see std::move_iterator<It>::operator->() is deprecated in C++20.
Which I am not sure what it means, perhaps it means that it can't be implemented, optionally, sometimes.
This question is relevant to this deprecation because in principle std::move_iterator<It>::operator->() could return ???* rather than pointer.
(Or return move_iterator<pointer>, although that will generate a infinite regress of ->() operators... unless there is a true pointer for r-values to terminate.)
NOTE 2:
A candidate missing row missing that I can imagine is:
| void& (not a thing) | void* |
but I think it is not related to the correspondence asked in the question, but a normal irregularity of void in the language.
There is no "symmetry between pointer and reference types". Or at least, there isn't intended to be one. You can interpret the language however you like, but any symmetry you see there is accidental.
In most ways lvalue and rvalue references work the same way. The principal differences are that you can distinguish between them by type (and can therefore overload functions or affect template instantiation based on them), and they have different rules for what objects they bind, particularly under overload resolution. The purpose of the distinction is to make the type system more accurately convey the value category of the source object, so that the user can know more about what is reasonable to do with the referenced object.
Pointers don't need to do this, so they don't need to make this distinction.
Is this table of correspondence meaningful in C++ (C++11 and beyond)?
It can be a useful mental model to think of references as a syntactically different way to achieve pointer semantics.
The crucial thing missing from your table is that the equivalent to a reference is not a pointer, but a const pointer:
| Reference | Pointer |
|-----------|----------------|
| T& | T* const |
| T const& | T const* const |
Note how this is a very rough approximation of the truth that quickly breaks down. All the caveats from #Nicol Bolas's answer apply here. Also, T const& will bind to temporaries, while T const* const will very much not.
Now about the missing row for T&&. The mental model for T&& here is a reference that you are always allowed to move from. In a function body, a T&& parameter to that function can be treated exactly as if it were a T& parameter, with special semantics only kicking in when it comes to moving. For a caller of said function on the other hand, T&& is unlike any of the known reference or pointer types in that it not only will bind to unnamed temporaries (like T const& does), but it will actually only bind to temporaries and nothing else.
You may be able to figure out for yourself why the equivalent concept for a pointer, while conceivable, would not have been a very useful addition to the language.
Regarding your note 1: Deprecated means that the thing is flagged for removal from the standard. While implementations will still support it for the time being, that is purely for backwards compatibility and new code should steer clear of using deprecated functionality. The reason this functionality on move_iterator should no longer be used is exactly the absence of the concept of a pointer-that-is-safe-to-use-from from the language.
Regarding your note 2: Due to historical reasons, another crucial difference between pointers and references is that pointer types are interconvertible to some degree. That is a pointer T* can be cast to a U* even if T and U are unrelated types. This behavior is by many considered a highly dangerous legacy from C and deemed largely undesirable. References do not allow such conversions in any case. This should give you a hint as to why void& is not a valid datatype in the language.
Related
I have read a few (pesudo) implementions of std::move(). And all are just casting away the reference of the parameter and then returning it as a rvalue reference.
It doesn't do anything more than that.
However, I am curious:
1. whether it does more than that.
2. whether standard explicitly states that so that the caller should be aware the side effect.
In the book The C++ Programming Language 4th edition, it states "move(x) marks x for destruction so that move(x) should be used with care".
Does it mean that there is a side effect imposed by the standard and so compiler can do certain optimization?
Thanks in advance.
Yes, that's exactly as it is described in the standard. In N4659 (which is last draft I found)
it says in §23.2.5
template <class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept;
Returns: static_cast<remove_reference_t<T>&&>(t)
It doesn't mark anything for destruction, and it doesn't change the object but object may be changed in function that accepts rvalue (such as move constructor, move assignment operator)
Yes, std::move is a bit of a misnomer as it doesn't actually move anything. It is used to indicate that an object may be "moved from".
It does this by casting the object to a T&&. cppreference states the return value is static_cast<typename std::remove_reference<T>::type&&>(t). (btw, that is exactly what VS2017 does)
I don't know precisely what the standard says on the matter.
Matrix<T, Dynamic, Dynamic>::InnerIterator doesn't seem to use the standard way (operator *) to dereference it.
It has a member function value().
I am rather surprised, as this would not interface well with third party algorithms, like STL algorithms.
Why it doesn't declare operator *? Even worse is that value() does not return by reference, so you can't change the underlying value of the pointed element. What is the proper way to use it?
This is because most of the times, the value alone in useless without the respective inner/row/column indices, as returned by index()/row()/col(). In other cases, you only care about the indices. Anyway, you can easily write a little wrapper to make it compatible with STL if needed.
Finally, if you want to modify the value, there is a valueRef() method returning by non const reference.
Consider a class with a member that can't be stored directly, e.g., because it does not have a default constructor, and the enclosing class's constructor doesn't have enough information to create it:
class Foo
{
public:
Foo(){} // Default ctor
private:
/* Won't build: no default ctor or way to call it's
non-default ctor at Foo's ctor. */
Bar m_bar;
};
Clearly, m_bar needs to be stored differently, e.g., through a pointer. A std::unique_ptr seems better, though, as it will destruct it automatically:
std::unique_ptr<Bar> m_bar;
It's also possible to use std::experimental::optional, though:
std::experimenatl::optional<Bar> m_bar;
My questions are: 1. What are the tradeoffs? and 2. Does it make sense to build a class automating the choice between them?
Specifically, looking at the exception guarantees for the ctor of std::unique_ptr and the exception guarantees for the ctor of std::experimental::optional, it seems clear that the former must perform dynamic allocation and deallocation - runtime speed disadvantages, and the latter stores things in some (aligned) memory buffer - size disadvantages. Are these the only tradeoffs?
If these are indeed the tradeoffs, and given that both types share enough of their interface (ctor, operator*), does it make sense to automate the choice between them with something like
template<typename T>
using indirect_raii = typename std::conditional<
// 20 - arbitrary constant
sizeof(std::experimental::optional<T>) >
20 + sizeof(std::exerimental::optional<T>)sizeof(std::unique_ptr<T>),
std::unique_ptr<T>,
std::experimental::optional<T>>::type;
(Note: there is a question discussing the tradeoffs between these two as return types, but the question and answers focus on what each conveys to the callers of the function, which is irrelevant for these private members.)
IMO there are other trade-offs at play here:
unique_ptr is not copyable or copy-assignable, while optional is.
I suppose one thing you could do is make indirect_RAII a class-type and conditionally add definitions to make it copyable by calling Bar's copy ctor, even when unique_ptr is selected. (Or conversely, disable copying when it's an optional.)
optional types can have a constexpr constructor -- you can't really do the equivalent thing with a unique_ptr at compile-time.
Bar can be incomplete at the time that unique_ptr<Bar> is constructed. It cannot be incomplete at the time that optional<Bar> is known. In your example I guess you assume that Bar is complete since you take its size, but potentially you might want to implement a class using indirect_RAII where this isn't the case.
Even in cases where Bar is large, you still may find that e.g. std::vector<Foo> will perform better when optional is selected than when unique_ptr is. I would expect this to happen in cases where the vector is populated once, and then iterated over many times.
It may be that as a general rule of thumb, your size rule is good for common use in your program, but I guess for "common use" it doesn't really matter which one you pick. An alternative to using your indirect_RAII type is, just pick one or the other in each case, and in places where you would have taken advantage of the "generic interface", pass the type as a template parameter when necessary. And in performance-critical areas, make the appropriate choice manually.
I'm a C++ senior programmer. I'm currently doing some Go programming. The only feature I really miss is the const qualifier. In go, if you want to modify an object, you pass its pointer. If you don't want to modify it, you pass it by value. But if the struct is big, you should pass it by pointer, which overrides the no-modification feature. Worse, you can pass an object by value, but if it contains a pointer, you can actually modify its contents, with terrible race condition dangers. Some language types like maps and slices have this feature. This happens in a language that's supposed to be built for concurrency. So the issue of avoiding modification is really non-existent in Go, and you should pass small objects that do not contain pointers (you must be aware that the object does not contain a pointer) by value, if they aren't gonna be modified.
With const, you can pass objects by const pointer and don't worrying about modification. Type-safety is about having a contract that allows speed and prevents type-related bugs. Another feature that does this too is the const qualifier.
The const type qualifier in C/C++ has various meanings. When applied to a variable, it means that the variable is immutable. That's a useful feature, and one that is missing from Go, but it's not the one you seem to be talking about.
You are talking about the way that const can be used as a partially enforced contract for a function. A function can give a pointer parameter the const qualifier to mean that the function won't change any values using that pointer. (Unless, of course, the function uses a cast (a const_cast in C++). Or, in C++, the pointer points to a field that is declared mutable.)
Go has a very simple type system. Many languages have a complex type system in which you enforce the correctness of your program by writing types. In many cases this means that a good deal of programming involves writing type declarations. Go takes a different approach: most of your programming involves writing code, not types. You write correct code by writing correct code, not by writing types that catch cases where you write incorrect code. If you want to catch incorrect code, you write analyzers, like go vet that look for cases that are invalid in your code. These kinds of analyzers are much much easier to write for Go than for C/C++, because the language is simpler.
There are advantages and disadvantages to this kind of approach. Go is making a clear choice here: write code, not types. It's not the right choice for everyone.
Please treat it as an expanded comment. I'm not any programming language designer, so can't go deep inside the details here, but will present my opinion as a long-term developer in C++ and short-term developer in Go.
Const is a non-trivial feature for the compiler, so one would have to make sure whether it's providing enough advantage for the user to implement it as well as won't sacrifice the simplicity of syntax. You might think it's just a const qualifier we're talking about, but looking at C++ itself, it's not so easy – there're a lot of caveats.
You say const is a contract and you shouldn't be able to modify it at any circumstances. One of your arguments against using read only interfaces is that you can cast it to original type and do whatever you want. Sure you can. The same way you can show a middle finger to the contract in C++ by using const_cast. For some reason it was added to the language and, not sure I should be proud of it, I've used it once or twice.
There's another modifier in C++ allowing you to relax the contract – mutable. Someone realised that const structures might actually need to have some fields modified, usually mutexes protecting internal variables. I guess you would need something similar in Go in order to be able to implement thread-safe structures.
When it comes simple const int x people can easily follow. But then pointers jump in and people really get consfused. const int * x, int * const x, const int * const x – these are all valid declarations of x, each with different contract. I know it's not a rocket science to choose the right one, but does your experience as a senior C++ programmer tell you people widely understand these and are always using the right one? And I haven't even mentioned things like const int * const * * * const * const x. It blows my mind.
Before I move to point 4, I would like to cite the following:
Worse, you can pass an object by value, but if it contains a pointer,
you can actually modify its contents
Now this is interesting accusation. There's the same issue in C++; worse – it exists even if you declare object as const, which means you can't solve the problem with a simple const qualifier. See the next point:
Per 3, and pointers, it's not so easy to express the very right contract and things sometimes get unexpected. This piece of code surprised a few people:
struct S {
int *x;
};
int main() {
int n = 7;
const S s = {&n}; // don't touch s, it's read only!
*s.x = 666; // wait, what? s is const! is satan involved?
}
I'm sure it's natural for you why the code above compiles. It's the pointer value you can't modify (the address it points to), not the value behind it. You must admit there're people around that would raise their eyebrow.
I don't know if it makes any point, but I've been using const in C++ all the time. Very accurate. Going mental about it. Not sure whether is has ever saved my ass, but after moving to Go I must admit I've never missed it. And having in mind all these edge cases and exceptions I can really believe creators of a minimalistic language like Go would decide to skip on this one.
Type-safety is about having a contract that allows speed and prevents
type-related bugs.
Agreed. For example, in Go, I love there're no implicit conversions between types. This is really preventing me from type-related bugs.
Another feature that does this too is the const qualifier.
Per my whole answer – I don't agree. Where a general const contract would do this for sure, a simple const qualifier is not enough. You then need a mutable one, maybe kind of a const_cast feature and still – it can leave you with misleading believes of protection, because it's hard to understand what exactly is constant.
Hopefully some language creators will design a perfect way of defining constants all over in our code and then we'll see it in Go. Or move over to the new language. But personally, I don't think C++'s way is a particularly good one.
(Alternative would be to follow functional programming paradigms, which would love to see all their "variables" immutable.)
This from Bjarne Stroustrup's The C++ Programming Language, Fourth Edition 3.3.2.
We didn’t really want a copy; we just wanted to get the result out of
a function: we wanted to move a Vector rather than to copy it.
Fortunately, we can state that intent:
class Vector {
// ...
Vector(const Vector& a); // copy constructor
Vector& operator=(const Vector& a); // copy assignment
Vector(Vector&& a); // move constructor
Vector& operator=(Vector&& a); // move assignment
};
Given that definition, the compiler will choose the move constructor
to implement the transfer of the return value out of the function.
This means that r=x+y+z will involve no copying of Vectors. Instead,
Vectors are just moved.As is typical, Vector’s move constructor is
trivial to define...
I know Golang supports traditional passing by value and passing by reference using Go style pointers.
Does Go support "move semantics" the way C++11 does, as described by Stroustrup above, to avoid the useless copying back and forth? If so, is this automatic, or does it require us to do something in our code to make it happen.
Note: A few answers have been posted - I have to digest them a bit, so I haven't accepted one yet - thanks.
The breakdown is like here:
Everything in Go is passed by value.
But there are five built-in "reference types" which are passed by value as well but internally they hold references to separately maintained data structure: maps, slices, channels, strings and function values (there is no way to mutate the data the latter two reference).
Your own answer, #Vector, is incorrect is that nothing in Go is passed by reference. Rather, there are types with reference semantics. Values of them are still passed by value (sic!).
Your confusion suppsedly stems from the fact your mind is supposedly currently burdened by C++, Java etc while these things in Go are done mostly "as in C".
Take arrays and slices for instance. An array is passed by value in Go, but a slice is a packed struct containing a pointer (to an underlying array) and two platform-sized integers (the length and the capacity of the slice), and it's the value of this structure which is copied — a pointer and two integers — when it's assigned or returned etc. Should you copy a "bare" array, it would be copied literally — with all its elements.
The same applies to channels and maps. You can think of types defining channels and maps as declared something like this:
type Map struct {
impl *mapImplementation
}
type Slice struct {
impl *sliceImplementation
}
(By the way, if you know C++, you should be aware that some C++ code uses this trick to lower exposure of the details into header files.)
So when you later have
m := make(map[int]string)
you could think of it as m having the type Map and so when you later do
x := m
the value of m gets copied, but it contains just a single pointer, and so both x and m now reference the same underlying data structure. Was m copied by reference ("move semantics")? Surely not! Do values of type map and slice and channel have reference semantincs? Yes!
Note that these three types of this kind are not at all special: implementing your custom type by embedding in it a pointer to some complicated data structure is a rather common pattern.
In other words, Go allows the programmer to decide what semantics they want for their types. And Go happens to have five built-in types which have reference semantics already (while all the other built-in types have value semantics). Picking one semantics over the other does not affect the rule of copying everything by value in any way. For instance, it's fine to have pointers to values of any kind of type in Go, and assign them (so long they have compatible types) — these pointers will be copied by value.
Another angle to look at this is that many Go packages (standard and 3rd-party) prefer to work with pointers to (complex) values. One example is os.Open() (which opens a file on a filesystem) returning a value of the type *os.File. That is, it returns a pointer and expects the calling code to pass this pointer around. Surely, the Go authors might have declared os.File to be a struct containing a single pointer, essentially making this value have reference semantics but they did not do that. I think the reason for this is that there's no special syntax to work with the values of this type so there's no reason to make them work as maps, channels and slices. KISS, in other words.
Recommended reading:
"Go Data Structures"
"Go Slices: Usage and Internals"
Arrays, slices (and strings): The mechanics of 'append'"
A thead on golang-nuts — pay close attention to the reply by Rob Pike.
The Go Programming Language Specification
Calls
In a function call, the function value and arguments are evaluated in
the usual order. After they are evaluated, the parameters of the call
are passed by value to the function and the called function begins
execution. The return parameters of the function are passed by value
back to the calling function when the function returns.
In Go, everything is passed by value.
Rob Pike
In Go, everything is passed by value. Everything.
There are some types (pointers, channels, maps, slices) that have
reference-like properties, but in those cases the relevant data
structure (pointer, channel pointer, map header, slice header) holds a
pointer to an underlying, shared object (pointed-to thing, channel
descriptor, hash table, array); the data structure itself is passed by
value. Always.
Always.
-rob
It is my understanding that Go, as well as Java and C# never had the excessive copying costs of C++, but do not solve ownership transference to containers. Therefore there is still copying involved. As C++ becomes more of a value-semantics language, with references/pointers being relegated to i) smart-pointer managed objects inside classes and ii) dependence references, move semantics solves the problem of excessive copying. Note that this has nothing to do with "pass by value", nowadays everyone passes objects by Reference (&) or Const Reference (const &) in C++.
Let's look at this (1) :
BigObject BO(big,stuff,inside);
vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BO);
Or (2)
vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BigObject(big,stuff,inside));
Although you're passing by reference to the vector vo, in C++03 there was a copy inside the vector code.
In the second case, there is a temporary object that has to be constructed and then is copied inside the vector. Since it can only be accessed by the vector, that is a wasteful copy.
However, in the first case, our intent could be just to give control of BO to the vector itself. C++17 allows this:
(1, C++17)
vector<BigObject> vo;
vo.reserve(1000000);
vo.emplace_back(big,stuff,inside);
Or (2, C++17)
vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BigObject(big,stuff,inside));
From what I've read, it is not clear that Java, C# or Go are exempt from the same copy duplication that C++03 suffered from in the case of containers.
The old-fashioned COW (copy-on-write) technique, also had the same problems, since the resources will be copied as soon as the object inside the vector is duplicated.
Stroustrup is talking about C++, which allows you to pass containers, etc by value - so the excessive copying becomes an issue.
In Go, (like in Delphi, Java, etc) when you pass a container type, etc they are always references, so it's a non-issue. Regardless, you don't have to deal with it or worry about in GoLang - the compiler just does what it needs to do, and from what I've seen thus far, it's doing it right.
Tnx to #KerrekSB for putting me on the right track.
#KerrekSB - I hope this is the right answer. If it's wrong, you bear no responsibility.:)