Re: Another approach to forward/move issues
Howard Hinnant wrote:
template<class T>
T moveable boo(const T moveable t)
In the example "t" is "value" - new value of caller parameter of "T"
type.
Also "boo" declared as returning value created by "move constructor"
instead
of "copy constructor".
{
//here end of "t" life-time
T moveable tmp(t);
tmp = 3;
//here end of "tmp" life-time
return tmp;
}
template <class T>
T boo(T&& t)
No, no, no. You are contradicts youself. Previously we have assumed that
"T&&" means: "t" is address of exernal "T", but now you are trying to
express with "T&&" value of exernal "T", you are trying to express
"sizeof(T)" with the help of "sizeof(T*)" - it is impossible.
Between the human language difference and the proposal syntax
difference, we are all struggling with clear communication.
Assuming _there is_ difference between the human language and the proposal
syntax. I do not think that can exist "human C++ language" and "C++ language
of proposal syntax".
For example, I could propose new data C++ type "universe". Speking "C++
language of proposal syntax" the "universe" is only one data type, that must
exist in C++: char, int, double, user defined casses - all can be
represented by the "universe":
universe foo(universe u)
{
universe tmp(u);
return tmp;
}
We can goto further, remove even the "universe", and write like this:
foo(u)
{
tmp(u);
return tmp;
}
But there is difference between the human language and the proposal syntax.
From your
comments above I believe the correct syntax transformation would just be:
template <class T>
T boo(T t)
{ ...
You have changed
T moveable boo(const T moveable t)
into
T boo(T t)
I suspect you think that "moveable" is the same as "non-const". This is one
of the "6 points of disagreement", the point has been quietly ignored by you
(the point remained without answer). No, in the message below you have
writeen a little about it.
Really, there are huge difference between:
T boo(T t)
and
T boo(const T t)
and
T boo(T moveable t)
and
T boo(const moveable T t)
This is really _4_ different data types of parameter, named "t". You are
agree or not?
Also between
T foo([const] T t)
and
T boo([const] moveable T t)
the difference is "what constructor (copy or move) will be implicitly used
to make t":
T src;
foo(src);
//here "copy constructor" will make "t" from "src"
//T(const T&);
boo(src);
//here "move constructor" will make "t" from "src"
//T(const moveable T&:(ctor;dtor));
You are correct that an rvalue reference is just like our existing
reference today in that it is just a pointer underneath.
Either "r-value" reference is only "moveable reference" or is only "moveable
value" - both case is not enough, because we need "moveable reference and
value" simultaneously.
And unfortunately, the current way of implementation of "move semantics with
the help of r-value reference", that has been selected, even in theory can
not be upgradeable to make complete support of the "move semantics", that
can be easy done in real compilers.
Also it is a big question: do we need to call "moveable" as "rvalue"? I do
not think so.
{
T tmp(std::move(t));
tmp = 3;
return tmp;
You just can not do "return tmp", because T::T(const T&) is private in
this
context - T is moveable only.
This is one of the key ideas from the current proposal that you are
missing, and this is likely my fault as it was not clearly described in
the "brief introduction" paper. Please see:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm#Moving%
20from%20local%20values
This explains that in the cases where RVO is legal by today's rules,
there is effectively an implicit cast to rvalue on the return statement.
What does "RVO" mean - r-value what?
ref>Copy constructor elision (NRVO) almost fixes this, but not quite
Assuming RVO is "copy constructor elision". (Below you have declared "RVO
(return value optimization)").
Thus in the example above, if T has a move constructor, that is what is
used to "copy" tmp out of boo(). This move may be elided via RVO. If T
does not have a move constructor, but does have a copy constructor, then
that will be used, or elided.
Are you really trying to find all possible cases where "implicit move
constructor" can be applied and then hard include the cases into language
instead of to give to user general tools to express what he wants?
Looking to expression
T boo()
{
T tmp(3);
return tmp;
}
user must guess, that T will be maked by "move constructor" (if move ctor
declared). But what user must do if he want to use "copy constructor" (in
spite of move ctor can be declared), as the C++ declaration normally
requires?
If we have no body (note it is onle of the 6 point of disagreement, quietly
ignored by your)
T boo();
can you detect, does "boo" return value will be created by "T copy ctor" or
by "T move ctor"?
Do you see that there is no tools to express correct and clear code, if
"moveable" will be based on the fact, that "Copy constructor can be elided"?
It is the same as if we invent hard included into language "const" data
type, because some C++ libraries has been written without "const", for
example like this:
inline T boo(T t)
{
T tmp(t);
return tmp;
}
here "t" will be implicitly const in C++, because correct code must assume
"t" is const, in order to not allocate new memory for "t", compiler will be
able to optimize "t", using "t" as alias or as reference of source variable.
Do you like the kind of "const"? The "r-value reference" is the same,
because it is impossible to express new data type as "little refinement of
old data type".
The logic resulting from this implicit cast results in an automatic
hierarchy of "move semantics" from best to worst:
* If you can elide the move/copy, do so (by present language rules)
* Else if there is a move constructor, use it
* Else if there is a copy constructor, use it
* Else the program is ill formed
No, we need to be able to declare move semantics _explicitly_ (and use the
semanics without std::move()). The set of rules is interesting as example of
implementation of rules of interaction of "copyable and moveable" data
types, but the rules can not be used as definition of moveable declaration.
Definition of "moveable declaration" and data optimization ("RVO") must be
separated!
By the way, the rules can be wrong for moveable data type included as
ordinary C++ data type, because we must not prefer "move" befor "copy", no
reasons to do it and "elide" is not the same as "move".
This enables types which are not copyable, but are movable, to be
returned from functions by value.
unique_ptr<int> p1;
unique_ptr<int> p2 = p1; // compile time error
unique_ptr<int> make_unique_ptr(); // function prototype
unique_ptr<int> p3 = make_unique_ptr(); // ok
C++ already have syntax idiom to declare data types without
"make_unique_ptr", and supporting the ordinary C++ syntax idiom is better
for any new regular stuff in comparison with irregular user defined one
unique_ptr<int> p1;
unique_ptr<int> p2 = p1; // compile time error
moveable unique_ptr<int> p3 = p1; // ok
This allows the return value
optimisation, which is more efficient than a move.
What is "the return value optimisation" and "move"?
RVO (return value optimization) is a license granted by the C++98
standard to avoid executing the copy constructor (in some cases) when a
value is returned from a function.
Well, return value optimization is one of the possible optimization of data
and must be separated from moveable data type proposal, at least to allow to
develop the stuffs independently!
I really suspect that we are much more in agreement than we realize. We
are differing in syntax, backwards compatibility concerns, generic
coding concerns, etc. But we have also come to some similar conclusions.
I am afraid we have some serious, base disagreements. I suspect, i have no
enough C++ knowledge to generate own proposal of C++ moveable, so i want to
convince you to change your proposal and your mind into correct way, into
way based on requirements that has been detected by me (and probably by
other users) in practical situations, not only from theoretical refinement
of RVO, ( i think theoretical because i have not seen nany users, operaiting
with rvalue/lvalue data types in their programs before).
The detected sources of moveable data type and detalied explanation of them
can be found on my page http://grizlyk1.narod.ru/cpp_new article #11-#15,
and among them
- moveable is ordinaty data type, unrelated to r-value term
- non-const is not the same as moveable
- compile time attributes is way to make moveable usage safe
- and so on
It is possible, that "r-value reference" with syntax "T&&", being subset of
ordinary moveable data type (but with special "r-value reference" rules),
can co-exist with ordinary moveable data type with syntax "moveable T&" (and
ordinary C++ rules), that can give to users more freedom to select its
design.
But if it is impossible to make C++ as huge collection of different moveable
behaviours, then regular ordinary moveable data type with syntax "moveable
T&" (and ordinary C++ rules) must be selected agains most other possible
representations.
a =< b;
This is actually the very syntax I first came up with too! :-) Daveed
Vandevoorde was kind enough to point out to me nearly 6 years ago:
On Jun 1, 2001, at 2:46 PM, Daveed Vandevoorde wrote:
To: C++ extensions mailing list
Message c++std-ext-4128
Howard Hinnant wrote:
[...]
struct A
{
A=<(A&) throw();
A& operator=<(A&) throw();
1. The expression "a =< b;" in my proposal is two operators
a.operator=( b.operator<() );
not single
a.operator=<(b);
The expression "a =< b;" often used as one of the way of data type local
override or for iterator-like access instead of "operator*()", because
iterator is kind of pointer. Normally we must write like this
moveable T a;
a = b;
//here move ctor used according to
//"a" declaration
2. Operator move (<) is "unary operator", similarly to "asterisk" (*), so
correct definition of "operator<"
any_type T::operator<()dtor;
or
friend any_type T::operator< (T const moveable&:(ctor;dtor));
I think the syntax will need revision. Since core issue 38 became a DR,
the sequence 'operator=<' can be the beginning of a template-id that
refers to a specialization of an operator= template.
Maybe, for "prefix unary operator move" can be used any: "<", "<-" and so
on. "<" is most short one. Sequence "<class A, class B<A>>" of template can
be confused with ">>" operator.
I think postfix binary "operator=<" is not needed for moveable, because move
constructor used according to moveable declaration of target data type.
We have a requirement of local explicit data type override, but
static_cast<> maybe can be used. Can we allow cast to copyable from
moveable? Do we need special moveable_cast<>?
The moveable_cast<> is similar to const_cast<> for "const" data type,
sometimes i think the names could be more clear as unconst_cast<> and
copyable_cast<> (by name of destination type, not source).
moveable T a;
//cast to copyable reference: copy ctor will be used
static_cast<T&>(a) = b;
moveable_cast<T&>(a) = b;
Daveed
It was John Maddock who first suggested the current syntax just a day
later:
On Jun 2, 2001, at 7:29 AM, John Maddock wrote:
A a1;
A a2(move(a1));
a1 = move(a2);
We need operators "move" and "swap" just because last is basical data
operation, exist in most CPU as high perfomans single opcode for POD data,
and first is basical operation under moveable data type (as assignment under
copyable data type). The absence of the operators in C++ can not be
explained by any rational rationale, can be only by "i do not want".
Do you want to write for copyable
a.std::assign( std::asteriks(b) );
insead of
a=*b;
Some major points:
* Compile time detection of double-move is not realistic.
And you already have made counting of the difficulties? The compile time
"detection of double-move" is not more difficult than "assignment to const"
or "variable is never used" compile time detections.
It simply
can not be done reliably enough in real world use cases.
The your opinion is contradicts to my concrete provements and is not based
on any concrete facts of "real world use cases".
The original
proposal recognizes this fact,
Can i read about details of the process of recognizing more?
and the danger of the double-move by
requiring syntax other than copy for all moves except when it is not
possible to get into a double-move situation. Thus programmers can more
easily audit their code for double-moves:
a = std::move(b);
.
c = std::move(b); // this is greppable
I think we need refuse from "const" also, because it makes compiler
difficult (becasue let's compiler give back all its forces to compile
templates, instead of "assignment to const" detection), because programmers
can more easily audit their code for assignment to const:
//non const
int b;
//const
int a=std::assing_to_const(b);
..
a=std::assing_to_const(b); // this is greppable
Nice?
I know, Stroustrup once wrote something like "C++ modifications can be
implemented by library", but the sentence can not be used totaly everywhere,
especially if the way will contradicts with regular C++ rules. There are
differneces between "moveable data type appearance" and "printf
replacement".
* We can't just change auto_ptr such that:
auto_ptr<A> p1;
auto_ptr<A> p2 = p1;
stops compiling. Yes, we would like to do that. No we can't. Vendors
would go out of business if they tried. So we have to "fix" auto_ptr by
starting over with a new name.
Who does argue? We need use correct syntax:
auto_ptr<A> p1;
moveable auto_ptr<A> p2 = p1;
now OK
* Generic algorithms need to work with types that are:
- only have copy constructors
- have both move and copy constructors
I do not argue, i can repeat from my page:
Appearence of new ordinary data type (owning rights of data type) turns the
situation from boolean state: "non-copyable/copyable" into more complex
'combinatoric' state: "non-copyable/moveble/copyable" with defined
relationship between bolean state: "moveble/copyable".
But "moveble" is not guilt, it is becasue of nature of "high perfomans
transfer of dynamic memory over auto memory boundaries combined with
automatic free of the memory on the auto memory boundaries".
and we want many algorithms to work with type that:
- have only move constructors
It isn't realistic to require overloaded generic algorithms for these
cases.
Let's people make the selection, maybe they have own generic algorithms
perfectly working for these cases? We must not reject any usefull general
design stuffs from language just because obsolete implementation of stdlib
can not work with them without modifications or can not use it completely.
The stdlib is not a Bible, so we must not design language in order to be
compartible with concrete implementation of any library. Correct user code,
working with interfaces of any library will not see any changes in library
implementation.
By the way, stdlib will continue to work without any modification on "C++
with moveable support", because moveable is orthogonal property, of course,
moveable data type can not be used just because stdlib does not support
them.
The same templated code needs to work for copyable types as
move-only types. For example we want both:
vector<unique_ptr<int>> and vector<string> to not require two completely
different implementations of vector.
And if you take that vector and
run over it with std::remove, the same algorithm ought to work for both.
Again, i do not argue, if vector<> can work with unique_ptr<int>, internals
of vector<> must be declared as using of moveable only:
template<class T>
class vector<T>
{
moveable T <memory>* data;
..
};
I am thinking - do we need to declare any template as strict copyable or
moveable:
template<copyable class T>
class vector<T>;
in order to detect error at the the point of declaration instead of
returning internals of hidden templated implementation:
template<class T>
void foo()
{
moveable T t;
vector<T> v;
error T must be copyable according to declaration
template<> class vector;
instead of messages that:
test.cpp: 20: \
instantiated from:\
lin/sd/sds/ds/ds/ds/dsd/s.cc: 303\
instantiated from:\
lin/sd/sds/ds/ds/ds/ds/f.cc: 30\
instantiated from:\
lin/sd/werwer.cc: 298\
instantiated from:\
lin/sd/sds/ds/ds/ds/ds/ds/dsd/s.cc: 30304\
: erorr: asdas:ASDASD:ASDa:ASdasd:ASda:Asda:Asd:data\
<asdas:ASDASD:ASDa:ASdasd:ASda:Asda,\
asdas:ASDASD:ASDa:ASdasd:ASda:Asda,\
asdas:ASDASD:ASDa:ASdasd:ASda:Asda\
& \
is private in the context
}
I can repeat, in my page the question of copyable and moveable interaction
placed in section "problems", but (unlike to moveable data type definitions
having no any problems) the problems of interacltion here _only_ because of
i have no any information to make correct definition of the rules of
interaction, because the rules have _random rationale_. We can declare any
rule, as we need by concrete requirements of interaction.
Anyway, you are trying to consider very complex examples of interaction of
copyable and moveable, and it seems to me, the way of consideration is
hiding the essence of the real problems of moveable data type by
difficulties of the examples of interaction as exmples itself.
Try consider my proposal of "regular moveable data type" step by step, from
#11 to #15, you will see, there are no any limitations for copyable and
moveable interaction.
* I think it is unwise to move from const-qualified types. A move is
allowed to change the source.
Returning to question of "const moveable". I have posted tons of examples
where difference between const and moveable has been shown. The followin
explanation has been taken from my page:
http://grizlyk1.narod.ru/cpp_new/#12
[quote]
NOTE: "Non-const" is not the same as "moveable".
It is very important thing to see the differences between "non-const" and
"moveable". And comparing operations "move" and "swap" we can easy to find
the differences.
I think some people are trying to declare moveable (auto_ptr for exmple) as
non-const in hope to warn user, that the moveable has unexpected, hidden
assignment to its internal state, after operation "move" (the "move"
implemented as overloading operation "copy") has executed.
- It is easy to see, that the warning is similar to refusing from static
type checking with the help of "void" speaking "we garantee nothing".
- Also it is easy to see, that non-const can not protect us from logically
wrong "operator move" has been applied twice to the non-const object (double
move). We can say, that move semantic required from C++ "once" support
rather than "non-const" support, it is really different things.
- So if assuming, that "operator move" (address extraction from auto_ptr)
can occur only once for the concrete moveable object (that must be garanteed
by programmer because C++ still can not do it), the moveable (auto_ptr) can
be const. The "once" is independent from "const".
- Also the moveable (auto_ptr) must be const if we want to disable explicit
assignment to the moveable (auto_ptr). Remove constness in order to make
only "move" is wrong idea and does not require by move semantic.
Non-const can be used for any purpose, but due to non-const can not protect
moveable from "double move", const must be used for standard purpose -
protect from explicit changes of external state of moveable (from assignment
for example). Consider the differences:
1.
template<T>
void swap_1(T& lhs, T& rhs)
{
const T dummy ( <lhs );
//ok
//here "const T" protect us from wrong assignment to temporary
dummy= <rhs; //compile time error
lhs= <rhs;
rhs= <dummy;
//error
//here compiler must generate error
//independent from constness of dummy
//because "double move" is "once" violation
<dummy;
}
2.
template<T>
void swap_2(T& lhs, T& rhs)
{
T dummy ( <lhs );
//error
//here compiler must generate error
//but "non-const T" can not protect us from wrong assignment to temporary
dummy= <rhs; //ok
lhs= <rhs;
rhs= <dummy;
//error
//here compiler must generate error
//independent from constness of dummy
//because "double move" is "once" violation
<dummy;
}
And the fact that "const dummy" does not strictly required for function
"swap", is not speaking that all other functions does not strictly require
const movable.
In opposite to "operator move", "operator swap" really require non-const to
its argumets, because (unlike to "move") we are going to use the swap
arguments after "operator swap" has been executed.
[/quote]
If you declare something const, then you
are saying you don't want it changed.
On your page:
ref> Moving from const objects (even rvalues) must be prohibited.
ref> It is very difficult to distinguish between a const and an rvalue
ref> in the current language (not impossible ... auto_ptr pulls it off).
The sentence "in the current language" - I think you have made a big mistake
trying to adapt term "rvalue" to "moveable" and looking on "moveable" from
"rvalue" side instead of side of ordinary data type. The "moveable" type is
very different from "copyable" as "const" from "non-cost".
The heroic attempt to "make moveable from void, doing nothing" will be only
new wrong point of C++, because i hope you know placement of free cheese.
PS:
I thanks you and other that you have a time and forces to answer.
But really (in spite of the your current answer is more closely to "answer
by essence") due to the technic of "ignoring all found unsuitable questions"
we are nearly month jumping on the same point where we was in february.
If it is not deliberate accidents, then the situation must be improved
maybe? At least we are spearing about C++ property, that can not give us
personally any profits or losses.
If it is deliberate strategy, in fact it means "r-value and other proposals
is not disputable" and i only can regret about the accident as for all other
known error points of C++, that can not give user abilities to do what he
wants and C++ can do.
I think (and have written many arguments), if even allow the "r-value
regerence" will exist in C++, the "r-value regerence" can not be used as
"moveable data type replacement", so we need "correct copyable and moveable
data type definitions" and "compile time attributes" to safe work with
"moveable data type".
--
Maksim A. Polyanin
http://grizlyk1.narod.ru/cpp_new
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]