why vector needs an assignable requirement for object T ?

A

abir

Hi
I am using an object with a const member like ,
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}
};
I am happy with default copy ctor ...
but as its member is const, i can't make it assignable.

if i have std::vector<Foo> FV;
FV v; v.push_back(Foo(1));
Now i can't store it in vector. push_back internally calls insert and
which somewhere needs fill.
Why it is required at all,?
I have a handcrafted vector class where push_back does not need
assignment (not even copy ctor, an move op is sufficient)
I don't know where it is required apart from v = Foo(3); or
equivalently *it = Foo(3); kind of statement.
even for insert at the mid i can transfer the mid elements at the
tail and copy /move construct the new elements at the raw memory at
mid.

Can anybody help me here?
Thanks
 
A

alfps

Hi
 I am using an object with a const member like ,
struct Foo{
    const int x_;
    explicit Foo(int x) : x_(x){}};

I am happy with default copy ctor ...
but as its member is const, i can't make it assignable.

if i have std::vector<Foo> FV;
FV v; v.push_back(Foo(1));
Now i can't store it in vector. push_back internally calls insert and
which somewhere needs fill.
Why it is required at all,?
I have a handcrafted vector class where push_back does not need
assignment (not even copy ctor, an move op is sufficient)
I don't know where it is required apart from v = Foo(3); or
equivalently *it = Foo(3); kind of statement.
 even for insert at the mid i can transfer the mid elements at the
tail and copy /move construct the new elements at the raw memory at
mid.


When you use a non-assignable element type you're violating the
promise that that type gives: if you have a pointer or reference or
iterator to such an element you have a right to expect that it won't
change.

Removing or inserting in a vector can however change the elements
beyond the removal/insertion point.

And there is no separate class for "constant vector".

In the end it's an issue of complexity.

std::vector would have to be more complicated to deal with the issue
of non-assignable element type (but note that the restriction to
assignable is just a specification bug for std::list, corrected in C+
+0x).


Cheers & hth.,

- Alf
 
A

abir

Hi
I am using an object with a const member like ,
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
I am happy with default copy ctor ...
but as its member is const, i can't make it assignable.
if i have std::vector<Foo> FV;
FV v; v.push_back(Foo(1));
Now i can't store it in vector. push_back internally calls insert and
which somewhere needs fill.
Why it is required at all,?
I have a handcrafted vector class where push_back does not need
assignment (not even copy ctor, an move op is sufficient)
I don't know where it is required apart from v = Foo(3); or
equivalently *it = Foo(3); kind of statement.
even for insert at the mid i can transfer the mid elements at the
tail and copy /move construct the new elements at the raw memory at
mid.


When you use a non-assignable element type you're violating the
promise that that type gives: if you have a pointer or reference or
iterator to such an element you have a right to expect that it won't
change.

I am not sure what promise the type need to give. The only promise i
can think of is that the container can rearrange the elements in any
memory in any order (for vector even the memory order is specified as
for any ith element &v[i+1] == &v+1)
Why the type need to promise that it need to be copy ctor'able apart
from that it can be moved inside the vector's memory (through copy,
through move, through direct construct or whatever other way
possible). I think c++0x removed that requirement, as in latest gcc
build i can write
v.push_back(1,2); for a struct which can take 2 args, and do inplace
construction.
or even a move ctor if i had externally constructed the object.
After that it can only rearrange the elements, or place them in some
new memory etc.
Why i need to give a promise that the vector (or any other container)
can change the state of the object which i requested it to store only?
one can always move an object with const state (or const object) in
another memory.
raw memory
Removing or inserting in a vector can however change the elements
beyond the removal/insertion point.

And there is no separate class for "constant vector".

In the end it's an issue of complexity.
It may be issue of complexity, but why it is a requirement?
 
A

alfps

I am not sure what promise the type need to give.

Per C++98 is must be copy constructible and copy assignable.

Per C++0x it must be move constructible and move assignable.

C++98 does not have the necessary language machinery to do move
semantics.

The only promise i
can think of is that the container can rearrange the elements in any
memory in any order (for vector even the memory order is specified as
for any ith element &v[i+1] == &v+1)
Why the type need to promise that it need to be copy ctor'able apart
from that it can be moved inside the vector's memory (through copy,
through move, through direct construct or whatever other way
possible).


It's not a question of how vector can be implemented to move bit
values around.

It's a question of type guarantees.

Let T be a copy constructible but non-assignable type. Then for the
purpose of achieving a kind of assignment effect you can do

T a( 1 );
T b( 2 );

// a = b
b.~T();
new( &b ) T(a);

But however well that works you have violated the type's non-
assignable constraint. If there is a pointer to 'a' somewhere, say,
the code holding that pointer does not expect 'a' to change. If type T
really supported assignment (change of value) it would not be
restricted in that way.

Cheers & hth.,

- Alf
 
J

James Kanze

I am using an object with a const member like ,
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
I am happy with default copy ctor ...
but as its member is const, i can't make it assignable.

Sure you can. You just can't change the value of x_ in the
assignment. Do you want to be able to change the value of x_ or
not? If so, don't declare it const. If not, not changing the
value means not changing the value, regardless of the expression
being used.
if i have std::vector<Foo> FV;
FV v; v.push_back(Foo(1));
Now i can't store it in vector. push_back internally calls
insert and which somewhere needs fill.
Why it is required at all,?

Why is what required? The standard requires that all types in
standard containers be assignable. This is because standard
containers use value semantics, and value semantics suppose
assignable. And in practice, many of the functions of the
containers do require assignable in order to work. (The
exception is node based containers, like std::list and the
associative containers. And the next version of the standard
will not require assignable for these.)
I have a handcrafted vector class where push_back does not need
assignment (not even copy ctor, an move op is sufficient)

What's a "move op"? The standard says that std::vector requires
assignable. It may be possible to implement some of the member
functions without using assignment, but a good implementation
will generally trigger a compiler error if assignment is not
supported, even if no member functions are ever called, e.g.:

#include <vector>

struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};

void
f()
{
std::vector< Foo > v ;
}

does not compile with my compiler (but it is undefined behavior,
so an error message is not guaranteed---it might compile, then
reformat your hard disk at run-time).
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
I don't know where it is required apart from v = Foo(3); or
equivalently *it = Foo(3); kind of statement.


The current version of the standard says that for all
containers, "The type of objects stored in these components must
meet the requirements of CopyConstructible types and the
additional requirements of Assignable types." No exceptions.
Failing to abide by this is undefined behavior. Good compilers
will generate an error at any attempt to instantiate the
component with a type which doesn't conform, but this is not
required by the standard; as far as the standard is concerned,
it is undefined behavior.
even for insert at the mid i can transfer the mid elements at
the tail and copy /move construct the new elements at the raw
memory at mid.

I'm not sure what you mean by "move construct". There is no
"move construct" in the current standard. (There will be in the
next version, and the requirements are being reworked to take
this and rvalue references into account. But it will
doubtlessly be awhile before compilers implement this.)
 
A

abir

I am using an object with a const member like ,
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
I am happy with default copy ctor ...
but as its member is const, i can't make it assignable.

Sure you can. You just can't change the value of x_ in the
assignment. Do you want to be able to change the value of x_ or
not? If so, don't declare it const. If not, not changing the
value means not changing the value, regardless of the expression
being used.
if i have std::vector<Foo> FV;
FV v; v.push_back(Foo(1));
Now i can't store it in vector. push_back internally calls
insert and which somewhere needs fill.
Why it is required at all,?

Why is what required? The standard requires that all types in
standard containers be assignable. This is because standard
containers use value semantics, and value semantics suppose
assignable. And in practice, many of the functions of the
containers do require assignable in order to work. (The
exception is node based containers, like std::list and the
associative containers. And the next version of the standard
will not require assignable for these.)
I have a handcrafted vector class where push_back does not need
assignment (not even copy ctor, an move op is sufficient)

What's a "move op"? The standard says that std::vector requires
assignable. It may be possible to implement some of the member
functions without using assignment, but a good implementation
will generally trigger a compiler error if assignment is not
supported, even if no member functions are ever called, e.g.:

#include <vector>

struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};

void
f()
{
std::vector< Foo > v ;
}

does not compile with my compiler (but it is undefined behavior,
so an error message is not guaranteed---it might compile, then
reformat your hard disk at run-time).
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
I don't know where it is required apart from v = Foo(3); or
equivalently *it = Foo(3); kind of statement.


The current version of the standard says that for all
containers, "The type of objects stored in these components must
meet the requirements of CopyConstructible types and the
additional requirements of Assignable types." No exceptions.
Failing to abide by this is undefined behavior. Good compilers
will generate an error at any attempt to instantiate the
component with a type which doesn't conform, but this is not
required by the standard; as far as the standard is concerned,
it is undefined behavior.

i know about the copy constructible requirement (wich can be solved
with the rvalue ref).
Don't know why the assignable requirement is there.
I'm not sure what you mean by "move construct". There is no
"move construct" in the current standard. (There will be in the
next version, and the requirements are being reworked to take
this and rvalue references into account. But it will
doubtlessly be awhile before compilers implement this.)
Yes, but in gcc c++0x mode (gcc 4.3) it uses std::fill and demands an
assignment op
(i.e X& operator(X& )
(while doesn't need the copy requirement, only X(X&& ) (is it called
move copy ? ) is sufficient)
Moreover for the point that it violates type's non assignable
constraint ( as pointed by Alf )

I am not sure why this violates the contract. contract works for the
type, not on the memory.
T a(1);
T b(2);
at this point a & b are object of type T, thus all the contracts
should be valid.
b.~T();
after this b is destroyed. It no longer an valid object, thus it has
no valid type.
b.anything is not going to work at this point, but that doesn't
violate the contract
as they are for type T.
new( &b ) T(a);
i am creating another object here with a copy from a at the location
previously pointed by b.
if i try to use old b & say this is a violation of contract, this is
definitely my fault.
in my system, if i write,
T* a = new T(1);
delete a;
T* b = new T(2);
at this point mostly i get a == b, as probably my system's new is
implemented in that way.
But that doesn't say it violated any contract of the object types
because as soon as i delete a,
i don't have any contract.
one auxiliary question,
can i have an existing c++ equiv of this one?

template<typename T,typename... Args>
void construct(T* p,const Args&&... a){
new(p) T(a...);
};
i.e forward the arguments in the construct site without having the
copy ctor (for T only)?
probably using some macros?
i am sure this group will be able to give a good answer.
thanks
 
N

Noah Roberts

abir said:
Hi
I am using an object with a const member like ,
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}
};
I am happy with default copy ctor ...
but as its member is const, i can't make it assignable.

if i have std::vector<Foo> FV;
FV v; v.push_back(Foo(1));
Now i can't store it in vector. push_back internally calls insert and
which somewhere needs fill.
Why it is required at all,?

Because the way in which vector works uses assignment a lot. If your
type doesn't have it then when you call those functions it will fail.

When I started learning I would often plug in a type that didn't have
assignment and it would work even with push_back. This is of course not
at all guaranteed to work for anyone on any compiler but it might.
 
H

Howard Hinnant

Per C++98 is must be copy constructible and copy assignable.

Per C++0x it must be move constructible and move assignable.

Not any more. The latest draft (http://www.open-std.org/jtc1/sc22/
wg21/docs/papers/2008/n2800.pdf) has:

template <ValueType T, Allocator Alloc = allocator<T> >
requires MoveConstructible<T>
class vector
{
public:
...
requires AllocatableElement<Alloc, T, const T&>
void push_back(const T& x);
requires AllocatableElement<Alloc, T, T&&>
void push_back(T&& x);
...
};

This roughly translates to what the OP expects.

Of course, this is just a draft. No telling how it will end up in the
FCD... ;-)

-Howard
 
J

James Kanze

I am using an object with a const member like ,
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
I am happy with default copy ctor ...
but as its member is const, i can't make it assignable.
#include <vector>
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
void
f()
{
std::vector< Foo > v ;
}
does not compile with my compiler (but it is undefined
behavior, so an error message is not guaranteed---it might
compile, then reformat your hard disk at run-time).
struct Foo{
const int x_;
explicit Foo(int x) : x_(x){}};
I don't know where it is required apart from v = Foo(3); or
equivalently *it = Foo(3); kind of statement.

The current version of the standard says that for all
containers, "The type of objects stored in these components
must meet the requirements of CopyConstructible types and
the additional requirements of Assignable types." No
exceptions. Failing to abide by this is undefined behavior.
Good compilers will generate an error at any attempt to
instantiate the component with a type which doesn't conform,
but this is not required by the standard; as far as the
standard is concerned, it is undefined behavior.


i know about the copy constructible requirement (wich can be
solved with the rvalue ref). Don't know why the assignable
requirement is there. even for insert at the mid i can
transfer the mid elements at the tail and copy /move construct
the new elements at the raw memory at mid.


The requirements are there because the standard doesn't want to
impose any specific implementation. Insert other than at the
end cannot be made to work correctly without assignment. More
fundamentally, the containers require a value semantic, and a
value semantic supposed assignment.
Yes, but in gcc c++0x mode (gcc 4.3) it uses std::fill and
demands an assignment op (i.e X& operator(X& )

What g++ does in c++0x mode is really whatever it wants, since
there isn't (yet) a C++0x. That's an experimental mode, for the
committee members to experiment with, and it shouldn't really be
used otherwise. (At least if it is what I think it is.) And
who knows what draft it corresponds to. (There are at least two
per year.)
(while doesn't need the copy requirement, only X(X&& ) (is it
called move copy ? ) is sufficient) Moreover for the point
that it violates type's non assignable constraint ( as pointed
by Alf)
I am not sure why this violates the contract. contract works
for the type, not on the memory.

The requirements concern the type. If the type isn't assignable
(your type isn't), then you've violated the requirements. I get
a compiler error from g++, just by trying to instantiate the
class.
T a(1);
T b(2);
at this point a & b are object of type T, thus all the
contracts should be valid.

Contracts of what? As long as you don't instantiate a standard
container over the object type, there's no problem.
b.~T();
after this b is destroyed. It no longer an valid object, thus
it has no valid type.

On the other hand, you're going to be in deep trouble when you
leave scope. Don't even think of doing such things.
b.anything is not going to work at this point, but that
doesn't violate the contract as they are for type T.

Again, what contract are you talking about?
new( &b ) T(a);
i am creating another object here with a copy from a at the
location previously pointed by b.
if i try to use old b & say this is a violation of contract,
this is definitely my fault.
in my system, if i write,
T* a = new T(1);
delete a;
T* b = new T(2);
at this point mostly i get a == b, as probably my system's new
is implemented in that way.

I don't know about the "probably". Some are, some aren't. For
debugging purposes, it's probably best to not reuse the memory
too quickly.
But that doesn't say it violated any contract of the object
types because as soon as i delete a, i don't have any
contract.
one auxiliary question,
can i have an existing c++ equiv of this one?
template<typename T,typename... Args>
void construct(T* p,const Args&&... a){
new(p) T(a...);};
i.e forward the arguments in the construct site without having
the copy ctor (for T only)?

Not today. I think that something like this is forseen for the
next version of the standard, but what it will look like exactly
is anybody's guess.
probably using some macros?
i am sure this group will be able to give a good answer.

I don't know. Sometimes, it's not clear what language you're
talking about, since much of what you're talking about isn't yet
C++, and we don't know what it will look like exactly once it
becomes C++. (Realistically, the way things are going, I
wouldn't count on any of this being usable before 2015. Maybe
later---look at export.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,217
Latest member
topweb3twitterchannels

Latest Threads

Top