I am trying to create a const constructor for my object A to avoid
repeat const_cast all over the place. (A is typically a shallow
wrapper of a vector... in fact it has another parameter, size, which I
am omitting here for simplicity).
I am using const_cast, and I think it is ok, because the object itself
will protect external access to member x via the const get() function.
The problem is that I would expect the const constructor to compile
only if the constructed object is const. But, as you can see it the
pseudo-code below, it compiles also for a NON-const object (at least
in VS). So I am introducing non safe constructor in the object A.
Can anybody tell what I am doing wrong?
Thanks
struct A {
A( int* a) : x(a) {}
const A( const int* a) : x( const_cast<int*>(a) ) {}
const int& get() const { return *x; }
int& get() { return *x; }
int *x;
};
int _tmain(int argc, _TCHAR* argv[])
{
int x = 3;
const int *p =&x;
A a1(p); // this const, ok
A a2(p); // this is not const, but it still compiles!!!
return 0;
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
fabio
|
6/9/2009 6:53:03 PM |
|
On Jun 10, 3:53 am, fabio.canni...@gmail.com wrote:
> struct A {
> A( int* a) : x(a) {}
> const A( const int* a) : x( const_cast<int*>(a) ) {}
>
> const int& get() const { return *x; }
> int& get() { return *x; }
>
> int *x;
>
> };
In an unfortunate limitation of C++, you can't do that. It's not safe.
That's why iterator and const_iterator of containers are separate
types with a lot of plumbing to make them interoperate.
Hmm ... const constructors would be an interesting language feature,
but I don't think you can make them work universally and safely.
So you will need a class A that holds int* and can only deal with non-
const integers, and a class ConstA that holds const int* and deals
with const integers. You should create an implicit conversion from A
to ConstA.
Sebastian
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
wasti
|
6/10/2009 1:07:53 PM
|
|
fabio.cannizzo@gmail.com wrote:
> I am trying to create a const constructor for my object A to avoid
> repeat const_cast all over the place. (A is typically a shallow
> wrapper of a vector... in fact it has another parameter, size, which I
> am omitting here for simplicity).
>
> I am using const_cast, and I think it is ok, because the object itself
> will protect external access to member x via the const get() function.
>
> The problem is that I would expect the const constructor to compile
> only if the constructed object is const. But, as you can see it the
> pseudo-code below, it compiles also for a NON-const object (at least
> in VS). So I am introducing non safe constructor in the object A.
>
> Can anybody tell what I am doing wrong?
There is no such thing as a const ctor in C++ (though some of us have
suggested that introducing them could be useful). Apparently VS just
ignores the const rather than giving you a diagnostic for your attempt
to declare a const ctor.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/10/2009 1:09:50 PM
|
|
wasti.redl@gmx.net wrote:
> On Jun 10, 3:53 am, fabio.canni...@gmail.com wrote:
>> struct A {
>> A( int* a) : x(a) {}
>> const A( const int* a) : x( const_cast<int*>(a) ) {}
>>
>> const int& get() const { return *x; }
>> int& get() { return *x; }
>>
>> int *x;
>>
>> };
>
> In an unfortunate limitation of C++, you can't do that. It's not safe.
> That's why iterator and const_iterator of containers are separate
> types with a lot of plumbing to make them interoperate.
I don't think this is the reason. What would copy-ctor and assignment
operator look like for such a fictional compact iterator?
It _may_ only be well defined for non-copyable and non-assignable
types, although I'm not sure there wouldn't be any problems even there.
Francis Glassborow wrote:
> There is no such thing as a const ctor in C++ (though some of us have
> suggested that introducing them could be useful).
I'm interested in a summary or a link related to this feature.
Thanks.
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/10/2009 7:02:12 PM
|
|
Francis Glassborow wrote:
> There is no such thing as a const ctor in C++ (though some of us have
> suggested that introducing them could be useful).
Dragan wrote:
> I'm interested in a summary or a link related to this feature.
> Thanks.
I thought a const constructor existed because of this article I found.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1995/N0798.htm
Regards.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Fabio
|
6/11/2009 4:25:38 AM
|
|
Francis Glassborow wrote:
> fabio.cannizzo@gmail.com wrote:
>> I am trying to create a const constructor for my object A to avoid
>> repeat const_cast all over the place. (A is typically a shallow
>> wrapper of a vector... in fact it has another parameter, size, which I
>> am omitting here for simplicity).
>>
>> I am using const_cast, and I think it is ok, because the object itself
>> will protect external access to member x via the const get() function.
>>
>> The problem is that I would expect the const constructor to compile
>> only if the constructed object is const. But, as you can see it the
>> pseudo-code below, it compiles also for a NON-const object (at least
>> in VS). So I am introducing non safe constructor in the object A.
>>
>> Can anybody tell what I am doing wrong?
> There is no such thing as a const ctor in C++ (though some of us have
> suggested that introducing them could be useful). Apparently VS just
> ignores the const rather than giving you a diagnostic for your attempt
> to declare a const ctor.
I'm glad the community now recognizes the necessity of const
constructors. Past discussions about const constructors and
post-constructors (another useful notion) have been ridiculed by people
locked on the apparent paradoxes (i.e. const constructors don't make
sense because you can't modify fields, and post-constructors would
suffer from infinite regression).
Andrei
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andrei
|
6/11/2009 4:27:27 AM
|
|
Andrei Alexandrescu wrote:
> Francis Glassborow wrote:
>> fabio.cannizzo@gmail.com wrote:
>>> I am trying to create a const constructor for my object A to avoid
>>> repeat const_cast all over the place. (A is typically a shallow
>>> wrapper of a vector... in fact it has another parameter, size, which I
>>> am omitting here for simplicity).
>>>
>>> I am using const_cast, and I think it is ok, because the object itself
>>> will protect external access to member x via the const get() function.
>>>
>>> The problem is that I would expect the const constructor to compile
>>> only if the constructed object is const. But, as you can see it the
>>> pseudo-code below, it compiles also for a NON-const object (at least
>>> in VS). So I am introducing non safe constructor in the object A.
>>>
>>> Can anybody tell what I am doing wrong?
>> There is no such thing as a const ctor in C++ (though some of us have
>> suggested that introducing them could be useful). Apparently VS just
>> ignores the const rather than giving you a diagnostic for your attempt
>> to declare a const ctor.
>
> I'm glad the community now recognizes the necessity of const
> constructors. Past discussions about const constructors and
> post-constructors (another useful notion) have been ridiculed by people
> locked on the apparent paradoxes (i.e. const constructors don't make
> sense because you can't modify fields, and post-constructors would
> suffer from infinite regression).
I'm not sure how was the necessity recognized in this thread.
And I still don't understand how could one design a class
that has copy/assignment and in the same time a const constructor.
const int x = 5;
const A a1(x);
A a2(a1);
a2.get() = 10; // ... and const_cast has bitten you data!!!
To OP - think about the equivalence of your class A with
pointers, and you'll understand the issue:
int const * const a1 <-> Const_A const b1
int const * a2 <-> Const_A b2
int * const a3 <-> A const b3
int * a4 <-> A b4
*a1 <-> b1.get()
a2 = a1 <-> b2 = b1
etc...
Anyway, I always hated that there _have_ to be 2 different classes for
a pointer to mutable and a pointer to const data. Do you have a nice
and logical solution to this problem, but without const_cast
(which lights a big red bulb!) or limiting to only non-copyable
clases? This would be great!
Also, I was able to live with making double classes, but later
I hit other issues when using these classes. These issues can
be simplified to the following patterns:
- Not being able to use shared_ptr as a covariant return type.
- Implicit casts are not transitive (for example can't
cast B -> Const_A when there is a path B -> A -> Const_A).
Does anyone else have the same experience, or was my design bad
and I'm talking nonsense?
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/11/2009 12:22:29 PM
|
|
> > > I am trying to create a const constructor for my object A to avoid
> > > repeat const_cast all over the place.
So, prior to your const constructor foray, you were looking at the
following pattern 'all over the place'?
void foo(int* pi) {
A myA(pi); // - 'all over the place'
// do stuff to A
}
void goo(const int* pic) {
// A myA(pic); - desired, but fails, so:
A myA(const_cast<int*>(pic)); // - 'all over the place'
// do stuff to A
}
If const constructors did exist, you would like to use:
void hoo(const int* pic) {
const A myA(pic);
// do stuff to A
}
Tucking the const_cast /neatly/ away in the const constructor?
Note that both 'goo' and 'hoo' require special treatment for
const.
> I'm glad the community now recognizes the necessity of const
> constructors.
Perhaps, but is there such a necessity here? Is it the handle (A)
or the body (int) that is const? Must it be both?
Consider:
template <class Body>
struct A {
A(Body* x) : x_(x) {}
Body& get() { return *x_; }
const Body& get() const { return *x_; }
Body* x_;
};
Then we can have:
void f(int* pi) {
A<int> myA(pi);
}
void g(const int* pic) {
A<const int> myA(pic);
}
Notice that 'g' must mention const just as 'goo' and 'hoo' do.
We can even have:
void h(int* pi, const int* pic) {
A<int> ai(pi);
A<const int> aci(pic);
const A<int> cai(pi);
const A<const int> caci(pic);
}
Whilst const constructors do appeal to me, they only solve the
'ai' vs 'caci' problem above. In general, with handles and
bodies, there are four possible const qualification combinations.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Nick
|
6/11/2009 10:36:20 PM
|
|
On 10 juin, 03:53, fabio.canni...@gmail.com wrote:
> Can anybody tell what I am doing wrong?
>
> Thanks
>
> struct A {
> A( int* a) : x(a) {}
> const A( const int* a) : x( const_cast<int*>(a) ) {}
>
> const int& get() const { return *x; }
> int& get() { return *x; }
>
> int *x;
>
> };
You're doing your design wrong.
You want A to be at the same time a mutable and an immutable view of
your data. That's just plainly weird.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Mathias
|
6/11/2009 10:38:35 PM
|
|
Thanks everybody.
As pointed out by many in this thread, the design is not correct.
Because of the difference between 'const int *' and 'int const *', I
would be able to copy construct a non const instance of the class. As
mentioned by Sebastian and others, there should be two classes, one
for const and the other for non const. However ... that is just so
terribly painful.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Fabio
|
6/12/2009 5:43:25 AM
|
|
On 12 juin, 14:43, Fabio <fabio.canni...@gmail.com> wrote:
> Because of the difference between 'const int *' and 'int const *',
They're the same.
You probably meant int * const.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Mathias
|
6/12/2009 4:14:39 PM
|
|
Andrei Alexandrescu wrote:
> Francis Glassborow wrote:
>> fabio.cannizzo@gmail.com wrote:
>>> I am trying to create a const constructor for my object A to avoid
>>> repeat const_cast all over the place. (A is typically a shallow
>>> wrapper of a vector... in fact it has another parameter, size, which I
>>> am omitting here for simplicity).
>>>
>>> I am using const_cast, and I think it is ok, because the object itself
>>> will protect external access to member x via the const get() function.
>>>
>>> The problem is that I would expect the const constructor to compile
>>> only if the constructed object is const. But, as you can see it the
>>> pseudo-code below, it compiles also for a NON-const object (at least
>>> in VS). So I am introducing non safe constructor in the object A.
>>>
>>> Can anybody tell what I am doing wrong?
>> There is no such thing as a const ctor in C++ (though some of us have
>> suggested that introducing them could be useful). Apparently VS just
>> ignores the const rather than giving you a diagnostic for your attempt
>> to declare a const ctor.
>
> I'm glad the community now recognizes the necessity of const
> constructors. Past discussions about const constructors and
> post-constructors (another useful notion) have been ridiculed by people
> locked on the apparent paradoxes (i.e. const constructors don't make
> sense because you can't modify fields, and post-constructors would
> suffer from infinite regression).
>
You must be keeping bad company :-) No one I know in the UK C++
Standards world has ever ridiculed const ctors. However they do have a
fairly low priority. Perhaps we should raise it.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/12/2009 4:17:24 PM
|
|
Dragan Milenkovic wrote:
> I'm not sure how was the necessity recognized in this thread.
> And I still don't understand how could one design a class
> that has copy/assignment and in the same time a const constructor.
>
> const int x = 5;
> const A a1(x);
> A a2(a1);
>
> a2.get() = 10; // ... and const_cast has bitten you data!!!
>
Sorry, but I do not understand the problem. Obviously you should not be
able to assign to a const object but what is the problem with copying
it? There is no reason in general why a copy of a const object should
itself be const (and no reason that it shouldn't). The problem arises if
your class has pointer and/or reference members but such classes are
good examples of where having const qualified ctors might be useful.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/12/2009 4:17:52 PM
|
|
Fabio wrote:
> Francis Glassborow wrote:
>> There is no such thing as a const ctor in C++ (though some of us have
>> suggested that introducing them could be useful).
>
> Dragan wrote:
>> I'm interested in a summary or a link related to this feature.
>> Thanks.
>
> I thought a const constructor existed because of this article I found.
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1995/N0798.htm
>
No that paper was just a proposal. Unfortunately it was poorly timed
because we were struggling with trying to get the first C++ Standard
ready for shipping. In addition the paper's author is extremely busy and
very rarely manages to attend C++ Standards meetings.
It might be worth resurfacing that paper when the current work has been
completed with a view to including const ctors in the version of C++
after the one we are currently working on.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/12/2009 4:18:21 PM
|
|
Fabio wrote:
> Thanks everybody.
>
> As pointed out by many in this thread, the design is not correct.
> Because of the difference between 'const int *' and 'int const *',
There is no difference :-( I think you meant between int const * and
int * const
I
> would be able to copy construct a non const instance of the class. As
> mentioned by Sebastian and others, there should be two classes, one
> for const and the other for non const. However ... that is just so
> terribly painful.
>
>
--
Note that robinton.demon.co.uk addresses are no longer valid.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/12/2009 4:18:24 PM
|
|
Francis Glassborow wrote:
> Dragan Milenkovic wrote:
>
>
>> I'm not sure how was the necessity recognized in this thread.
>> And I still don't understand how could one design a class
>> that has copy/assignment and in the same time a const constructor.
>>
>> const int x = 5;
>> const A a1(x);
>> A a2(a1);
>>
>> a2.get() = 10; // ... and const_cast has bitten you data!!!
>>
>
> Sorry, but I do not understand the problem. Obviously you should not be
"A" is what OP was trying to design...
Fabio wrote:
> struct A {
> A( int* a) : x(a) {}
> const A( const int* a) : x( const_cast<int*>(a) ) {}
>
> const int& get() const { return *x; }
> int& get() { return *x; }
>
> int *x;
> };
The problem is "A a2(a1)" creates a mutable object from a const one,
which allows "x" to be written to. But see below first...
> able to assign to a const object but what is the problem with copying
> it? There is no reason in general why a copy of a const object should
> itself be const (and no reason that it shouldn't). The problem arises if
> your class has pointer and/or reference members but such classes are
> good examples of where having const qualified ctors might be useful.
I have no idea who is saying what. I'm totally confused.
Shall we start from the beginning? You speak about necessity of const
constructors. My assumption based on your words is that you would
create an additional const copy-ctor that would forbid the problematic
line "A a2(a1)", right? Would you be so kind as to write a complete
class based on OP's one? I still don't get how the whole picture
looks like. Someone provided a link to a proposal for const-ctor,
but it didn't cover these issues I ask about.
Thanks.
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/13/2009 3:49:07 PM
|
|
Dragan Milenkovic wrote:
> Francis Glassborow wrote:
>> Dragan Milenkovic wrote:
>>
>>
>>> I'm not sure how was the necessity recognized in this thread.
>>> And I still don't understand how could one design a class
>>> that has copy/assignment and in the same time a const constructor.
>>>
>>> const int x = 5;
>>> const A a1(x);
>>> A a2(a1);
>>>
>>> a2.get() = 10; // ... and const_cast has bitten you data!!!
>>>
>>
>> Sorry, but I do not understand the problem. Obviously you should not be
>
> "A" is what OP was trying to design...
>
> Fabio wrote:
>> struct A {
>> A( int* a) : x(a) {}
>> const A( const int* a) : x( const_cast<int*>(a) ) {}
>>
>> const int& get() const { return *x; }
>> int& get() { return *x; }
>>
>> int *x;
>> };
>
>
> The problem is "A a2(a1)" creates a mutable object from a const one,
> which allows "x" to be written to. But see below first...
>
Oh, my mistake. I thought we were talking about well designed classes. I
would need a really good justification for allowing a class to copy a
pointer by value. I really do not want classes sharing members that way.
Indeed having a pointer member is one of the main indicators that your
class needs a user written copy ctor.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/14/2009 4:18:45 AM
|
|
Dragan wrote:
> Shall we start from the beginning?
I work with vectors (1D data structures), matrices (2D data
structures), and cubes (3D data structures). Often I want to extract
sub-sections from these structures. For instance, a sub-matrix from a
matrix. However I do not want to do deep-copies. So I am trying to
write shallow wrappers for this objects based on memory pointers and
indices, which are very fast to construct and usually get inlined.
However, I would like these shallow wrappers to respect const-ness of
the originary underlying objects.
The code below refers to a vector, which is fairly trivial. One
possible shallow wrapper entails of just a pointer and an integer (the
size of the vector).
class shallow_vector
{
size_t size;
int *data;
public:
// ... constructors and other methods
};
I thought that if const constructors did exist, perhaps it would be
possible to use them in conjunction with const_cast to write a wrapper
class which has the features below.
const int const_vec[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // this is
a const vector
int vec[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // this is a non-
const vector
const shallow_vector subvector1( const_vec+3, 3 ); // assign
const to const object: I would like this to succeed
shallow_vector subvector2( &const_vec[3], 3 ); // assign const to
non-const object: I would like this to fail
const shallow_vector subvector3( subvector1 ); // assign const to
const object: I would like this to succeed
shallow_vector subvector4( subvector1 ); // assign const to non-
const object: I would like this to fail
const shallow_vector subvector5( vec+3, 3 ); // assign non-const
to non-const object: I would like this to succeed
shallow_vector subvector6( &vec[3], 3 ); // assign non_const to
non-const object: I would like this to succeed
const shallow_vector subvector7( subvector6 ); // assign non-const to
const object: I would like this to succeed
shallow_vector subvector8( subvector6 ); // assign non-const to non-
const object: I would like this to succeed
However, even if a const constructors existed, I am not sure how clean
the solution would be. Also there would be need to consider assignment
operators.
Dragan wrote:
> My assumption based on your words is that you would create an additional
const copy-ctor that would forbid the problematic line "A a2(a1)", right?
Yes, this was the intention. But there should be two copy-ctor, one to
disallow "A a2(a1)" and one to allow "const A a2(a1)".
As suggested above, the only clean solution seems to be the creation
of two wrappers, one const (const_shallow_vector) ad one non-const
(shallow_vector), with implicit type-casting from the non-const one to
the const one. Probably the non const class should also have private
constructor to prevent copy-construct from const objects. However this
is very painful, because lot of methods would be duplicated in the two
classes (all the const methods).
class shallow_vector
{
size_t size;
int *data;
shallow_vector( const shallow_vector& ); // private to prevent
assignment from const to non-const
public:
// ... other constructors and other const and non-const methods
};
class const_shallow_vector
{
size_t size;
const int *data; // pointer to const int!!!
public:
const_shallow_vector( const shallow_vector& v ) : size(v.size),data
(v.data) {} // allow implicit typecasting
// ... other constructors and other const methods
};
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Fabio
|
6/15/2009 8:06:27 AM
|
|
Francis Glassborow wrote:
> Dragan Milenkovic wrote:
>> "A" is what OP was trying to design...
>>
>> Fabio wrote:
>>> struct A {
>>> A( int* a) : x(a) {}
>>> const A( const int* a) : x( const_cast<int*>(a) ) {}
>>>
>>> const int& get() const { return *x; }
>>> int& get() { return *x; }
>>>
>>> int *x;
>>> };
>>
>>
>> The problem is "A a2(a1)" creates a mutable object from a const one,
>> which allows "x" to be written to. But see below first...
>>
> Oh, my mistake. I thought we were talking about well designed classes. I
> would need a really good justification for allowing a class to copy a
> pointer by value. I really do not want classes sharing members that way.
> Indeed having a pointer member is one of the main indicators that your
> class needs a user written copy ctor.
Actually, we _were_ talking about well designed classes. But no one that
is advocating const-ctors has provided an example of such a well defined
class. I'm very interested to seeing one, or a reference to a document
or a discussion. The link to an earlier proposal didn't provide
all the answers. Namely, operator= is not mentioned anywhere!?!
The only designs that were mentioned in this thread are Fabio's class A
and iterators. Both have a pointer-like semantics, so this is why
I fail to see the mentioned "necessity" and how would it all work.
In fact, almost all designs where I needed a functionality like this
fall under either pointer-like or wrapper classes.
Thanks for any further explanation.
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/15/2009 8:09:38 AM
|
|
Francis Glassborow wrote:
> Dragan Milenkovic wrote:
> > Francis Glassborow wrote:
> >> Dragan Milenkovic wrote:
>
> >>> I'm not sure how was the necessity recognized in this thread.
> >>> And I still don't understand how could one design a class
> >>> that has copy/assignment and in the same time a const constructor.
>
> >>> const int x = 5;
> >>> const A a1(x);
> >>> A a2(a1);
>
> >>> a2.get() = 10; // ... and const_cast has bitten you data!!!
>
> >> Sorry, but I do not understand the problem. Obviously you should not be
>
> > "A" is what OP was trying to design...
>
> > Fabio wrote:
> >> struct A {
> >> A( int* a) : x(a) {}
> >> const A( const int* a) : x( const_cast<int*>(a) ) {}
>
> >> const int& get() const { return *x; }
> >> int& get() { return *x; }
>
> >> int *x;
> >> };
>
> > The problem is "A a2(a1)" creates a mutable object from a const one,
> > which allows "x" to be written to. But see below first...
>
> Oh, my mistake. I thought we were talking about well designed classes. I
> would need a really good justification for allowing a class to copy a
> pointer by value. I really do not want classes sharing members that way.
> Indeed having a pointer member is one of the main indicators that your
> class needs a user written copy ctor.
Perhaps it is the context of the discussion that is driving your
statement; if not, it seems possibly a bit extreme to me. Do you
consider this example random number generator class
class GaussRNG {
double mean_ ;
double variance_ ;
UniformRNG * uniformRNG_ ;
public :
GaussRNG (
double mean, double variance, UniformRNG & uniformRNG )
: mean_(mean), variance_(variance), uniformRNG_(&uniformRNG)
{ }
double next ( ) {
double uniform = uniformRNG_->next() ;
// required transformation to specified gaussian
...
}
...
} ;
usage of a member pointer poor design and/or indicating the need
for a user defined copy ctor?
I find member pointers a common way of sharing resources and one
that often does not require custom ctor nor custom operator=.
Also can someone please supply an example showing the usefulness
of const constructor that does not involve the classic confusion
of mistaking "T * const" for "T const *" as in the op? Thanks.
KHD
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Keith
|
6/15/2009 10:50:58 AM
|
|
On 12 June, 06:38, Mathias Gaunard <loufo...@gmail.com> wrote:
> You're doing your design wrong.
> You want A to be at the same time a mutable and an immutable view of
> your data. That's just plainly weird.
I disagree, I think it's very sensible. A "foo" presents some data, a
"const foo" presents some immutable version of some data. Here's an
example straight from the STL:
#include <vector>
void mutate(const std::vector<int>& v)
{
v[0] = 0; //Ill formed
}
a const vector<int> is immutable, vector<int> is not. I don't see why
this is strange for slice/reference objects but not for value objects.
Besides, the const qualifier has nice readability to it, and the
original posters example is nicely consistent. Pass a const foo& to a
function and you can't modify foo being referenced. Pass in a const
foo*, you can't modify the foo being pointed to. So, it would be
reasonable that a const SliceOfFoo& wouldn't allow you to modify the
chunk of foo being pointed to, but that is not the case.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/15/2009 10:54:19 AM
|
|
Edward Rosten wrote:
> On 12 June, 06:38, Mathias Gaunard <loufo...@gmail.com> wrote:
>
>> You're doing your design wrong.
>> You want A to be at the same time a mutable and an immutable view of
>> your data. That's just plainly weird.
>
> I disagree, I think it's very sensible. A "foo" presents some data, a
> "const foo" presents some immutable version of some data. Here's an
> example straight from the STL:
>
> #include <vector>
>
> void mutate(const std::vector<int>& v)
> {
> v[0] = 0; //Ill formed
> }
>
> a const vector<int> is immutable, vector<int> is not. I don't see why
> this is strange for slice/reference objects but not for value objects.
> Besides, the const qualifier has nice readability to it, and the
> original posters example is nicely consistent. Pass a const foo& to a
> function and you can't modify foo being referenced. Pass in a const
> foo*, you can't modify the foo being pointed to. So, it would be
> reasonable that a const SliceOfFoo& wouldn't allow you to modify the
> chunk of foo being pointed to, but that is not the case.
IMHO, Mathias was speaking only in the context of the desired design.
Of course you can have const propagate from the object to its data,
just as in vector. You can have many designs with respect to
const/mutable qualifications of objects.
But you can _not_ have the combination of: the same class representing
both const and mutable data that is only references but not copied
itself, unless you give up on the copy semantics of the class
(or at least the assignment operator).
You might think it would be reasonable, but the goals are contradictory.
If you want to keep copy semantics and want a copy to point to the same
data, one const qualifier is simply not enough to represent the design.
You need two const qualifiers just as it is with pointers...
Here is a fictional design that might work:
class A {
public:
A(int * x);
otherconst A(int const * x);
A(A const & rhs);
otherconst A(A otherconst const A & rhs);
A & operator=(A const & rhs);
A otherconst & operator=(A otherconst const & rhs);
};
with implicit conversions (as with pointers):
A -> A otherconst -> A otherconst const
A -> A const -> A otherconst const
I will repeat my argument:
It is _not_ the limitation of C++, the problem is the design that would
require more than one constness qualification to be logical (whether in
the form of a keyword, or as it is currently - another class).
Just think about it - one const qualifier goes away when you copy
an object, so why did you tie something that remains const to it?
And if you make copy-ctor construct only a const copy of an object,
you still have to do something about operator=. Prohibiting it
is simply not in the spirit of C++. What's even worse, it would
hurt my feelings!
Now... I'm very eager to see the current proposal of const constructors,
and use cases that are meant to be solved...
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/16/2009 7:47:03 AM
|
|
On 15 juin, 19:54, Edward Rosten <Edward.Ros...@gmail.com> wrote:
> On 12 June, 06:38, Mathias Gaunard <loufo...@gmail.com> wrote:
>
> > You're doing your design wrong.
> > You want A to be at the same time a mutable and an immutable view of
> > your data. That's just plainly weird.
>
> I disagree, I think it's very sensible. A "foo" presents some data, a
> "const foo" presents some immutable version of some data. Here's an
> example straight from the STL
You're confusing data and views to data.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Mathias
|
6/16/2009 7:47:42 AM
|
|
On 15 June, 18:50, Keith H Duggar <dug...@alum.mit.edu> wrote:
> Also can someone please supply an example showing the usefulness
> of const constructor that does not involve the classic confusion
> of mistaking "T * const" for "T const *" as in the op? Thanks.
You may think this is an instance of the classic mistake, or you may
not, but it is hard to get nice slice objects. You can always write a
ConstSlice object, but that may well require you to write a lot of
internals twice which is bad. It also is not as nice as/not the same
as using a const X* to slice a raw array, and that's bad because
there's then a penalty for using user-defined types as opposed to
builtin ones.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/16/2009 7:47:52 AM
|
|
Francis Glassborow escribi�:
> fabio.cannizzo@gmail.com wrote:
>> I am trying to create a const constructor for my object A to avoid
<...>
> There is no such thing as a const ctor in C++ (though some of us have
> suggested that introducing them could be useful).
<..>
I would love not only const constructors, but fully cv-qualified
constrcutors,
that is, volatile constructors and (mostly as a side-effect) const
volatile contructors.
Regards,
Zara
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Juan
|
6/16/2009 8:17:09 AM
|
|
On 16 Jun., 14:47, Dragan Milenkovic <dra...@plusplus.rs> wrote:
>
> Now... I'm very eager to see the current proposal of const constructors,
> and use cases that are meant to be solved...
>
> --
Please see the recent thread "non-const refs to const objects" in this
group for a discussion of const constructors.
Francis Glassborough brought the idea up and I mused about it a
little, until he basically declared the idea too revolutionary to get
realized ;-)
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
McNepp
|
6/16/2009 10:47:34 AM
|
|
On 16 June, 15:47, Mathias Gaunard <loufo...@gmail.com> wrote:
> On 15 juin, 19:54, Edward Rosten <Edward.Ros...@gmail.com> wrote:
>
> > On 12 June, 06:38, Mathias Gaunard <loufo...@gmail.com> wrote:
>
> > > You're doing your design wrong.
> > > You want A to be at the same time a mutable and an immutable view of
> > > your data. That's just plainly weird.
>
> > I disagree, I think it's very sensible. A "foo" presents some data, a
> > "const foo" presents some immutable version of some data. Here's an
> > example straight from the STL
>
> You're confusing data and views to data.
No, you're introducing a distinction. Why should a const view not be
const like data? A pointer can be either a slice or a piece of data. A
const Foo* is like an immutable Foo, regardless of whether it is a
slice or some data. For user defined slices as opposed to raw
pointers, this isn't the case since the conse ends up on the right of
the * not the left. In other words, user defined types behave
differently to builtin ones. I do not think this is a good thing.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/17/2009 7:36:51 PM
|
|
Edward Rosten wrote:
> On 16 June, 15:47, Mathias Gaunard <loufo...@gmail.com> wrote:
>> On 15 juin, 19:54, Edward Rosten <Edward.Ros...@gmail.com> wrote:
>>
>>> On 12 June, 06:38, Mathias Gaunard <loufo...@gmail.com> wrote:
>>>> You're doing your design wrong.
>>>> You want A to be at the same time a mutable and an immutable view of
>>>> your data. That's just plainly weird.
>>> I disagree, I think it's very sensible. A "foo" presents some data, a
>>> "const foo" presents some immutable version of some data. Here's an
>>> example straight from the STL
>> You're confusing data and views to data.
>
> No, you're introducing a distinction. Why should a const view not be
> const like data? A pointer can be either a slice or a piece of data. A
> const Foo* is like an immutable Foo, regardless of whether it is a
> slice or some data. For user defined slices as opposed to raw
> pointers, this isn't the case since the conse ends up on the right of
> the * not the left. In other words, user defined types behave
> differently to builtin ones. I do not think this is a good thing.
No, they don't.
typedef int * B;
const B <=> int * const
They behave the same. The only difference being that you can
add a "left-side const" to a pointer where you see fit.
OTOH, you could use const_A (a const version of your
user-defined type) where you see fit, so they still
do behave exactly the same. The obvious example is an iterator.
Anyway, I have already tried to explain the mentioned distinction
in my previous post. There is even a fictional (ugly? stupid?)
language feature that would allow adding a "left-side const".
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/18/2009 6:26:20 AM
|
|
On Jun 16, 10:47 am, Edward Rosten <Edward.Ros...@gmail.com> wrote:
> On 15 June, 18:50, Keith H Duggar <dug...@alum.mit.edu> wrote:
>
> > Also can someone please supply an example showing the usefulness
> > of const constructor that does not involve the classic confusion
> > of mistaking "T * const" for "T const *" as in the op? Thanks.
>
> You may think this is an instance of the classic mistake, or you may
> not,
I did not say it "is an instance of the classic mistake". I said
that it *involved* the mistake. In other words I do not think it
is necessarily a mistake to want const to propagate deeper than
it does in standard C++. The mistake is to *confuse* "T * const"
and "T const *" since it is important to know what we are trying
to achieve exactly.
> but it is hard to get nice slice objects. You can always write a
So what? How hard something is to achieve is a separate issue as
far as correctness of the logical and conceptual issues goes.
> ConstSlice object, but that may well require you to write a lot of
> internals twice which is bad. It also is not as nice as/not the same
> as using a const X* to slice a raw array, and that's bad because
> there's then a penalty for using user-defined types as opposed to
> builtin ones.
I agree const issues are some of the developing parts of C++ that
deserve continued thought and design. And in fact there have been
a number of nice debates in this forum regarding const'ness.
By the way, here is something that might (and I emphasize might
because I know it is only a small part of the puzzle) help your
implementations come along a bit more smoothly.
Instead of using built-in pointer you can define your own smart
pointer that propagates const from pointer to the pointee. For
example following Marco Manfredini's suggestion:
http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/1b9089fe78baebc3/f77e036fe877d75e#9628debea037c32e
template<class T>
class aggregated
{
T * value
public :
T const * operator -> ( ) const { return value ; }
T * operator -> ( ) { return value ; }
...
} ;
Thus making "T * const" propagate to "T const * const".
KHD
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Keith
|
6/18/2009 3:37:34 PM
|
|
On 18 June, 14:26, Dragan Milenkovic <dra...@plusplus.rs> wrote:
> No, they don't.
>
> typedef int * B;
> const B <=> int * const
>
> They behave the same. The only difference being that you can
> add a "left-side const" to a pointer where you see fit.
That's my point! You can get different behavior from built-in types as
compared to user defined ones. You can use a pointer to represent a
slice and left side const does what you expect. You can not make a
slice object and have left side const doing what you expect.
These may be the same:
typedef int * B
const B <=> int * const
These are not:
void foo(const double* d);
void foo(const ArraySilce& s);
> OTOH, you could use const_A (a const version of your
> user-defined type) where you see fit, so they still
> do behave exactly the same. The obvious example is an iterator.
Yes, and the result is ugly and looks/behaves differently from a raw
pointer. It also requires you to implement your code twice. The
language is very, very close: you _almost_ don't have to duplicate the
code. This strongly implies to me that there is a lack of generality.
It almost allows you do write the class once, but instead you have to
duplicate loads of code. Code duplication is IMO bad.
> Anyway, I have already tried to explain the mentioned distinction
> in my previous post. There is even a fictional (ugly? stupid?)
> language feature that would allow adding a "left-side const".
I fully understand the distinction.
-Ed
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/18/2009 3:37:49 PM
|
|
Edward Rosten wrote:
> On 18 June, 14:26, Dragan Milenkovic <dra...@plusplus.rs> wrote:
>
>> No, they don't.
>>
>> typedef int * B;
>> const B <=> int * const
>>
>> They behave the same. The only difference being that you can
>> add a "left-side const" to a pointer where you see fit.
>
> That's my point! You can get different behavior from built-in types as
> compared to user defined ones. You can use a pointer to represent a
> slice and left side const does what you expect. You can not make a
> slice object and have left side const doing what you expect.
>
> These may be the same:
>
> typedef int * B
> const B <=> int * const
>
> These are not:
>
> void foo(const double* d);
> void foo(const ArraySilce& s);
Maybe the terminology is the problem in our communication.
My statement is that both custom classes and built-in pointers
behave the same when the _same_ operation is applied. Adding
left-side const is a different operation than adding right-side const.
void foo(const double * d);
void foo(ArraySlice const & s);
These two function parameters had different const-qualifications
applied, so their behavior is different.
Anyway, here is a version of ArraySlice that you might like.
It can be implemented with SFINAE, but that gets ugly.
With "concepts" on the way, it gets a lot cleaner.
template <typename Element>
class ArraySlice {
public:
explicit ArraySlice(Element * a, int from, int to)
: m_a(a+from), m_len(to-from) {}
template <typename RhsElement>
requires Same<Element, RhsElement>
|| Same<Element, const RhsElement>
ArraySlice(ArraySlice<RhsElement> const & rhs)
: m_a(rhs.m_a), m_len(rhs.m_len) {}
template <typename RhsElement>
requires Same<Element, RhsElement>
|| Same<Element, const RhsElement>
ArraySlice & operator=(ArraySlice<RhsElement> const & rhs)
{
m_a=rhs.m_a; m_len=rhs.m_len;
return *this;
}
Element & operator[](int index) const;
private:
Element * m_a;
int m_len;
};
Now here is the equivalence:
void foo(const double * d);
void foo(ArraySlice<const double> s);
double * d1;
const double * d2 = d1;
ArraySlice<double> s1;
ArraySlice<const double> s2 = s1;
The behavior is exactly the same. You can apply const
anywhere you want. There is an implicit conversion from
ptr-to-mutable to ptr-to-const. And no duplication of code.
Forgive me if I made a mistake with respect to concepts,
since I'm fairly new (zero experience). Maybe it should be
"typename std::add_const<RhsElement>::type".
But... you get my point...
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/18/2009 7:42:44 PM
|
|
On Jun 18, 10:42 pm, Dragan Milenkovic <dra...@plusplus.rs> wrote:
> Edward Rosten wrote:
> > On 18 June, 14:26, Dragan Milenkovic <dra...@plusplus.rs> wrote:
>
> >> No, they don't.
>
> >> typedef int * B;
> >> const B <=> int * const
>
> >> They behave the same. The only difference being that you can
> >> add a "left-side const" to a pointer where you see fit.
>
> > That's my point! You can get different behavior from built-in types as
> > compared to user defined ones. You can use a pointer to represent a
> > slice and left side const does what you expect. You can not make a
> > slice object and have left side const doing what you expect.
>
> > These may be the same:
>
> > typedef int * B
> > const B <=> int * const
>
> > These are not:
>
> > void foo(const double* d);
> > void foo(const ArraySilce& s);
>
> Maybe the terminology is the problem in our communication.
To me the primary problem seems to be that ER is confusing *type*
with *declaration syntax*. The essential point you are making is
that given U is a built-in type and T is a user defined type then
const U * = const T *
U const * = T const *
U * const = T * const
...
where = should be taken to mean "semantically equivalent". Said
another way one can substitute a user defined type T for a built-
in type U in an identical declaration with equivalent results.
ER provided *different* declarations.
ER thinks that because string "ArraySlice" has semantic meaning
to *him*, then when T = "ArraySlice" something special happens.
Of course it does not. C++ does not care what the type's string
name means to ER nor anyone else; and the name means absolutely
nothing as far as built-in versus UDT differences with const.
Now the declaration syntax for a built-in pointer to const data
view type is
T const *
while the declaration syntax for a user defined const data view
type would be
class TConstPtr { } ;
or whatever suitable name one chooses. Note T can be either a
UDT or built-in type such as int
int const *
class intConstPtr { } ;
and that makes no difference. Finally, the differences between
the declaration syntax of "built-in pointer to const" and "user
defined pointer to const" is no more significant than the fact
that "class" and "enum" types have different declaration syntax
nor that built-in double and int have no declarations at all!
KHD
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Keith
|
6/18/2009 11:17:17 PM
|
|
On 19 Jun., 00:37, Edward Rosten <Edward.Ros...@gmail.com> wrote:
> On 18 June, 14:26, Dragan Milenkovic <dra...@plusplus.rs> wrote:
>
> > typedef int * B;
> > const B <=> int * const
>
> > They behave the same. The only difference being that you can
> > add a "left-side const" to a pointer where you see fit.
>
> That's my point! You can get different behavior from built-in types as
> compared to user defined ones.
But there is no difference, really. Please read this example
carefully:
typedef int* ptr_t;
typedef std::list<int>::iterator listiter_t;
const ptr_t === int*const #1
const listiter_t === std::list<int>::iterator const #2
#1 is a const pointer to a non-const int. #2 is a const iterator to a
non-const int. It's exactly the same behaviour.
But it's unfortunate that the STL uses names like "const iterator". In
STL-land "const iterator" means that it's an iterator for const value
types. The iterator itself is not const.
> You can use a pointer to represent a
> slice and left side const does what you expect.
Well, the const in "const int*" applies to the int and not to the
pointer. It's a DIFFERENT pointer type much like you can provide
DIFFERENT user-defined iterators.
> These may be the same:
>
> typedef int * B
> const B <=> int * const
>
> These are not:
>
> void foo(const double* d);
> void foo(const ArraySilce& s);
Of course not. This comparison is nonsense. See below.
> It almost allows you do write the class once, but instead you have to
> duplicate loads of code. Code duplication is IMO bad.
You can parameterize the type of elements your ArraySlice points to to
avoid code duplication:
void foo(const double* d);
void foo(ArraySilce<const couble> s);
would be the equivalent. (!)
> > Anyway, I have already tried to explain the mentioned distinction
> > in my previous post. There is even a fictional (ugly? stupid?)
> > language feature that would allow adding a "left-side const".
>
> I fully understand the distinction.
I don't think you do.
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SG
|
6/19/2009 9:47:59 AM
|
|
Andrei Alexandrescu wrote:
> Francis Glassborow wrote:
>> fabio.cannizzo@gmail.com wrote:
>>> I am trying to create a const constructor for my object A to avoid
>>> repeat const_cast all over the place. (A is typically a shallow
>>> wrapper of a vector... in fact it has another parameter, size, which I
>>> am omitting here for simplicity).
>>>
>>> I am using const_cast, and I think it is ok, because the object itself
>>> will protect external access to member x via the const get() function.
>>>
>>> The problem is that I would expect the const constructor to compile
>>> only if the constructed object is const. But, as you can see it the
>>> pseudo-code below, it compiles also for a NON-const object (at least
>>> in VS). So I am introducing non safe constructor in the object A.
>>>
>>> Can anybody tell what I am doing wrong?
>> There is no such thing as a const ctor in C++ (though some of us have
>> suggested that introducing them could be useful). Apparently VS just
>> ignores the const rather than giving you a diagnostic for your attempt
>> to declare a const ctor.
>
> I'm glad the community now recognizes the necessity of const
> constructors. Past discussions about const constructors and
> post-constructors (another useful notion) have been ridiculed by people
> locked on the apparent paradoxes (i.e. const constructors don't make
> sense because you can't modify fields, and post-constructors would
> suffer from infinite regression).
Could you elaborate what problem const constructors solve please?
--
Max
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Maxim
|
6/19/2009 10:21:48 AM
|
|
On 18 June, 23:37, Keith H Duggar <dug...@alum.mit.edu> wrote:
> > but it is hard to get nice slice objects. You can always write a
>
> So what? How hard something is to achieve is a separate issue as
> far as correctness of the logical and conceptual issues goes.
Sorry, what I meant by "hard" is that you have to essentially write it
twice in order to get it correct.
> > ConstSlice object, but that may well require you to write a lot of
> > internals twice which is bad. It also is not as nice as/not the same
> > as using a const X* to slice a raw array, and that's bad because
> > there's then a penalty for using user-defined types as opposed to
> > builtin ones.
>
> I agree const issues are some of the developing parts of C++ that
> deserve continued thought and design. And in fact there have been
> a number of nice debates in this forum regarding const'ness.
>
> By the way, here is something that might (and I emphasize might
> because I know it is only a small part of the puzzle) help your
> implementations come along a bit more smoothly.
I think we may be in violent agreement.
> Instead of using built-in pointer you can define your own smart
> pointer that propagates const from pointer to the pointee. For
> example following Marco Manfredini's suggestion:
>
> http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thr...
>
> template<class T>
> class aggregated
> {
> T * value
> public :
> T const * operator -> ( ) const { return value ; }
> T * operator -> ( ) { return value ; }
> ...
> } ;
The difficult here is when it comes to copying aggregated. You need to
be able to copy them to (for instance) return one from a function. At
this point, you can copy construct an aggregate from a const
aggregate, much as you can copy construct an int from a const int. At
that point, the code forgets that T* is meant to be const. One can
jiggle it around so that you can only copy const aggregates properly,
but not aggregates. Neither is a great solution.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/19/2009 10:22:19 AM
|
|
On 19 Jun., 04:42, Dragan Milenkovic wrote:
>
> Anyway, here is a version of ArraySlice that you might like.
> It can be implemented with SFINAE, but that gets ugly.
> With "concepts" on the way, it gets a lot cleaner.
>
> template <typename Element>
> class ArraySlice {
> public:
> explicit ArraySlice(Element * a, int from, int to)
> : m_a(a+from), m_len(to-from) {}
>
> template <typename RhsElement>
> requires Same<Element, RhsElement>
> || Same<Element, const RhsElement>
> ArraySlice(ArraySlice<RhsElement> const & rhs)
> : m_a(rhs.m_a), m_len(rhs.m_len) {}
>
> template <typename RhsElement>
> requires Same<Element, RhsElement>
> || Same<Element, const RhsElement>
> ArraySlice & operator=(ArraySlice<RhsElement> const & rhs)
> {
> m_a=rhs.m_a; m_len=rhs.m_len;
> return *this;
> }
>
> Element & operator[](int index) const;
>
> private:
> Element * m_a;
> int m_len;
>
> };
There are a couple of issues:
(1) SameType<Element, RhsElement> is not necessary because the
compiler
will generate a non-templated default copy-ctor
(2) SameType<Element, const RhsElement> is actually the wrong
direction.
SameType<const Element, RhsElement> is the right one.
(2) "||" for constraining templates is not part of the current draft.
(3) You forgot to declare ArraySlice<T> to be a friend of
ArraySlice<U> for T!=U.
A conceptized version of a user-defined pointer wrapper can be written
like this:
template<typename T>
class my_pointer
{
public:
template<typename> friend class my_pointer;
my_pointer(T* p) : ptr(p) {}
template<typename U> requires std::Convertible<U*,T*>
my_pointer(my_pointer<U> const& x) : ptr(x.ptr) {}
T* operator->() const {return ptr;}
T& operator*() const {return *ptr;}
private:
T* ptr;
};
A C++98 version would look like this:
#include <iostream>
/* enable_if<T,U,R>::type is R
* <==> T is const U and U is non-const
*/
template<typename T, typename U, typename R = char>
struct enable_if_cnc {};
template<typename U, typename R>
struct enable_if_cnc<const U, U, R> {typedef R type;};
template<typename T>
class my_pointer
{
public:
template<typename> friend class my_pointer;
my_pointer(T* p) : ptr(p) {}
template<typename U>
my_pointer(my_pointer<U> x,
typename enable_if_cnc<T,U>::type=0)
: ptr(x.ptr) {}
T* operator->() const {return ptr;}
T& operator*() const {return *ptr;}
private:
T* ptr;
};
int main()
{
int i = 23;
my_pointer<int> pi = &i;
my_pointer<const int> pci = pi;
*pi = 42;
std::cout << *pci << '\n';
}
The function enabler enable_if_cnc<T,U> is not exactly a translation
of Convertible<U*,T*> but it's the one I use for my 2D and 3D raster
container "locators". You can easily replace this with something like
template<typename U>
my_pointer(my_pointer<U> x,
typename enable_if< convertible<U*,T*>::value >::type=0)
: ptr(x.ptr) {}
if you want.
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SG
|
6/19/2009 10:22:27 AM
|
|
SG wrote:
> There are a couple of issues:
>
> (1) SameType<Element, RhsElement> is not necessary because the
> compiler will generate a non-templated default copy-ctor
Silly me...
> (2) SameType<Element, const RhsElement> is actually the wrong
> direction. SameType<const Element, RhsElement> is the right one.
For Element = T and RhsElement = const T we get:
SameType<const Element, RhsElement>
<=>SameType<const T, const T>
....which would make ArraySlice<T> initialized from ArraySlice<const T>.
Aha! You got confused, too! :-D
Thanks for all the other corrections.
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/19/2009 5:42:42 PM
|
|
On 19 juin, 19:22, SG <s.gesem...@gmail.com> wrote:
> The function enabler enable_if_cnc<T,U> is not exactly a translation
> of Convertible<U*,T*> but it's the one I use for my 2D and 3D raster
> container "locators". You can easily replace this with something like
>
> template<typename U>
> my_pointer(my_pointer<U> x,
> typename enable_if< convertible<U*,T*>::value >::type=0)
> : ptr(x.ptr) {}
>
> if you want.
That is incorrect, because
1) the first argument of enable_if should be a meta-function, not a
boolean (that would be enable_if_c)
2) the value of the enable_if meta-function is void by default, which
obviously is not allowed to specify the type of the second argument to
the constructor.
3) the actual type trait is named is_convertible, not convertible.
You need to write
typename enable_if<is_convertible<U*, T*>, void*>::type
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Mathias
|
6/19/2009 5:46:22 PM
|
|
On Jun 11, 7:27 am, Alexandrescu <SeeWebsiteForEm...@erdani.org>
wrote:
> Francis Glassborow wrote:
> > There is no such thing as a const ctor in C++ (though some of us have
> > suggested that introducing them could be useful). Apparently VS just
> > ignores the const rather than giving you a diagnostic for your attempt
> > to declare a const ctor.
>
> I'm glad the community now recognizes the necessity of const
> constructors. Past discussions about const constructors and
> post-constructors (another useful notion) have been ridiculed by people
> locked on the apparent paradoxes (i.e. const constructors don't make
> sense because you can't modify fields, and post-constructors would
> suffer from infinite regression).
Please provide some examples of where const constructors are
a "necessity". Neither the op nor the thread McNepp pointed to
provide such examples. This thread came down to the usual const
propagation arguments and confusion about "T const *" versus
"T * const" and the other thread was about undefined behavior
resulting from modifying const objects (a problem which const-
constructors would not address anyhow).
So, do you have some clear use cases where const constructors
are a "necessity" or even "useful"? Any links to a proposal?
KHD
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Keith
|
6/20/2009 2:11:06 AM
|
|
On 20 Jun., 02:46, Mathias Gaunard wrote:
> On 19 juin, 19:22, SG wrote:
>
> > The function enabler enable_if_cnc<T,U> is not exactly a translation
> > of Convertible<U*,T*> but it's the one I use for my 2D and 3D raster
> > container "locators". You can easily replace this with something like
>
> > template<typename U>
> > my_pointer(my_pointer<U> x,
> > typename enable_if< convertible<U*,T*>::value >::type=0)
> > : ptr(x.ptr) {}
>
> > if you want.
>
> That is incorrect, [...]
Well, I didn't provide enable_if nor convertible nor did I use any
boost::/tr1:: qualifications, so you, would not now whether it was
incorrect or not. It was just incomplete, but it certainly brought my
point across.
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SG
|
6/20/2009 2:15:09 AM
|
|
On 20 Jun., 02:42, Dragan Milenkovic wrote:
> SG wrote:
>
> > (2) SameType<Element, const RhsElement> is actually the wrong
> > direction. SameType<const Element, RhsElement> is the right one.
>
> For Element = T and RhsElement = const T we get:
>
> SameType<const Element, RhsElement>
> <=> SameType<const T, const T>
>
> ...which would make ArraySlice<T> initialized from ArraySlice<const T>.
> Aha! You got confused, too! :-D
Yeah, you're right. Sorry, for the trouble. I even thought that I got
the enable_if_cnc ("cnc" = const, non-const) function enabler stuff
wrong for a moment after I hit "send". But right now, it looks OK to
me.
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SG
|
6/20/2009 2:15:40 AM
|
|
Keith H Duggar wrote:
>
> So, do you have some clear use cases where const constructors
> are a "necessity" or even "useful"? Any links to a proposal?
>
There is no existing proposal other than the very old one from Kevlin
Henney. I am sure that were there more than places where they would be
nice that there would have been further proposals. However I am sure
that Kevlin would not have gone to the trouble of writing a paper had he
not considered them to be of some use. And knowing the quality of
Kevlin's understanding of the art of programming I am confident that
there would be uses.
Anyway the issue is largely academic for now. The time to repropose them
is some years in the future (after we get the next version of C++ done
and dusted)
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/21/2009 12:03:10 AM
|
|
>
> Instead of using built-in pointer you can define your own smart
> pointer that propagates const from pointer to the pointee. For
> example following Marco Manfredini's suggestion:
>
> http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/1b9089fe78baebc3/f77e036fe877d75e#9628debea037c32e
>
> template<class T>
> class aggregated
> {
> T * value
> public :
> T const * operator -> ( ) const { return value ; }
> T * operator -> ( ) { return value ; }
> ...
> } ;
>
> Thus making "T * const" propagate to "T const * const".
>
This is quite nice. However it still leaves the problem of
inizialization, where the source is a const T*.
So const_cast would still be needed, is it?
Fabio
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Fabio
|
6/21/2009 10:49:46 AM
|
|
Francis Glassborow wrote:
> Keith H Duggar wrote:
>
>>
>> So, do you have some clear use cases where const constructors
>> are a "necessity" or even "useful"? Any links to a proposal?
>>
>
> There is no existing proposal other than the very old one from Kevlin
> Henney. I am sure that were there more than places where they would be
> nice that there would have been further proposals. However I am sure
> that Kevlin would not have gone to the trouble of writing a paper had he
> not considered them to be of some use. And knowing the quality of
> Kevlin's understanding of the art of programming I am confident that
> there would be uses.
>
> Anyway the issue is largely academic for now. The time to repropose them
> is some years in the future (after we get the next version of C++ done
> and dusted)
Hey! This is quite different statement than what both you and
Andrei Alexandrescu said earlier in this thread. May I remind you?
"some of us have suggested that introducing them could be useful",
"necessity of const constructors". You got us thinking that
there is something going on. I even thought this refers
to the OP's problem, therefore the whole (endless) const correctness
discussion. Anyway, doesn't matter who said what...
The proposal Kevlin Henney wrote doesn't address all issues,
and falsely advertises const constructors as a solution
for wrappers and proxy classes (which is equvalent to OP's problem).
But it is not. Even the best of us make mistakes, it is
not a measure of someone's quality. The proposal is from 1995 anyway.
Now, I will try to summarize what I have learned about the issue.
There seem to be two different proposals for const constructors.
The first one is the one mentioned above. Although it can not be
used for wrappers and custom pointers, the first advertized
usage is correct and might be useful.
const Foo foo(args);
Since no mutator function can (or at least _should_) be invoked
on 'foo', it can have a different representation than a non-const
version. Sometimes it's possible/useful, sometimes it isn't. The
one thing to be careful is whether 'Foo' is copyable and
is it possible to make a copy from the const version. Kevlin gave
a good example, where the class creates a "changelog" for all
modifications. A const version needs not initialize this functionality,
and making a copy (if desirable) can still be done.
The other proposal addresses const-correctness of constructors.
const Foo {
public:
explicit Foo(Bar * bar) {
bar->register(this);
}
void mutator_func();
};
const Bar {
public:
void register(Foo * foo) {
foos.push_back(foo);
}
void mutate() {
foos[0].mutator_func();
}
private:
std::vector<Foo *> foos;
};
Bar bar;
const Foo foo(&bar);
bar.mutate(); // UB AFAIK
The proposal is to have const constructors where 'this' would be
a pointer to const Foo. You could still initialize fields, but only
through initializers. The rules would be the same as for
const/mutable member functions:
- You can invoke a const constructor for both mutable and const
objects.
- You can invoke a non-const constructor _only_ for mutable objects.
So, in order to create a 'const Foo', the constructor would
need to have a const-qualifier, but this would not allow it to
call Bar::register which takes only "Foo *".
However, this would break existing code. And I'm not sure if it breaks
anything else in the logic. Though, on first glance it seems sound...
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/21/2009 10:51:43 AM
|
|
On Jun 21, 1:49 pm, Fabio <fabio.canni...@gmail.com> wrote:
> > Instead of using built-in pointer you can define your own smart
> > pointer that propagates const from pointer to the pointee. For
> > example following Marco Manfredini's suggestion:
>
> >http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thr...
>
> > template<class T>
> > class aggregated
> > {
> > T * value
> > public :
> > T const * operator -> ( ) const { return value ; }
> > T * operator -> ( ) { return value ; }
> > ...
> > } ;
>
> > Thus making "T * const" propagate to "T const * const".
>
> This is quite nice. However it still leaves the problem of
> inizialization, where the source is a const T*.
> So const_cast would still be needed, is it?
Sorry, I don't know what problem you are referring to. Please
post a minimal example showing the problem. If you mean the op:
int x = 3 ;
const int * p = &x ;
A a(p) ; // this is not const, but it still compiles!!!
I would be writing
int x = 3 ;
const int * p = &x ;
aggregate<int const> a(p) ;
instead (since in that code I know at the point of instantiating
a that I'm instantiating it with T const *). Note that <T const>
applies to any smart pointer. The purpose of the aggregate class
above is to give the following behavior
aggregate<int> a(p) ;
*a = 5 ; // ok just as int *
...
aggregate<int> const ac(a) ;
*ac = 7 ; // compile error just as int const *
But hopefully you have some minimal realistic cleaned up example
of exactly the problem you are trying to solve. Anyhow, I do not
see a need for const_cast anywhere in the examples above.
KHD
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Keith
|
6/21/2009 7:05:06 PM
|
|
On 19 June, 07:17, Keith H Duggar <dug...@alum.mit.edu> wrote:
> and that makes no difference. Finally, the differences between
> the declaration syntax of "built-in pointer to const" and "user
> defined pointer to const" is no more significant than the fact
> that "class" and "enum" types have different declaration syntax
> nor that built-in double and int have no declarations at all!
This is the point that we fundemantally disagree on. You are assuming
incorrectly that I am confused about how to declare const types in C+
+. My point is this. Take the code:
const double * a_pointer;
const ADoublePointer another_pointer;
Because of the way const interacts with * you can not get a user
defined class to have similar properties to double* for instance. I
know full well that double const* would behave similarly. It also
means that slices and data behave in very different ways. For slices
of immutable data you need:
void foo(const slice<const double>& );
And the slice class has to know how to convert a slice<double> to a
slice<const double>. If you want to work on a non-sliced version of
the immutable data you must instead write eg:
void foo(const vector<double>&);
Of course if you just use raw pointers, the following single piece of
code works admirably for both:
void foo(const double*);
This is an inconsistency. Why should working on a reference to
immutable data work differently if the class holding the data has copy
or reference semantics? How you write the code depends on the details
of the copy constructor, even though no copying is going on. Also it
works consistently if you stick to raw pointers instead of user
defined types. This means IMO that C++ is lacking a mechanism for
flexible propagation of constness.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/22/2009 9:34:39 AM
|
|
On 19 June, 17:47, SG <s.gesem...@gmail.com> wrote:
> > These may be the same:
>
> > typedef int * B
> > const B <=> int * const
>
> > These are not:
>
> > void foo(const double* d);
> > void foo(const ArraySilce& s);
>
> Of course not. This comparison is nonsense. See below.
No it is not nonsense. You keep telling me what the rules of C++ are.
However one still writes:
void foo(const double* d);
and one can still not write this:
void foo(const ArraySilce& s);
The fact that ArraySlice may be a typedef double* is besides the
point. The point is that the first function is not equivalent to the
second. We agree on this. It also means that you can not get user
defined types to behave like builtin ones, because there is no way of
making the second one behave like the first. The fact that you can
make builtin types behave like user defined one is entirely besides
the point. Consider this: constructing builtin arrays has a nice
syntax. Constructing user-defined array like objects does not. The way
of fixing this is to introduce sequence constructors. Of course, you
can simply make the builtin types behave like the user-defined ones by
never using the nice, handy, easy to read syntax. You can probably
tell which of the two alternatives I prefer.
> > It almost allows you do write the class once, but instead you have to
> > duplicate loads of code. Code duplication is IMO bad.
>
> You can parameterize the type of elements your ArraySlice points to to
> avoid code duplication:
>
> void foo(const double* d);
> void foo(ArraySilce<const couble> s);
>
> would be the equivalent. (!)
I know that. But if you have a corresponding struct, eg Array with
copy semantics you would have to write instead:
void foo(const Array<double>&);
Look at the following two functions:
void foo(const Array<double>&);
void foo(ArraySilce<const couble> s);
Does that not look horribly inconsistent to you? It does to me.
My argument is that you get a horrible inconsistency with user-defined
types that you do not get with built-in ones (pointers). Repeatedly
telling me that I can't get what I want within the rules of C++ is not
a counter argument.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/22/2009 9:35:21 AM
|
|
> aggregate<int const> a(p) ;
Ok, I misunderstood the example. My silly.
So we have
aggregate<int const>
and
aggregate<int>
However, how do we define a constructor from aggregate<int> to
aggregate<const int>?
I would like to be able to initialize a const view from a non const
one.
I posted a detailed exanation of what I am trying to do early in this
post. Anyway, my question now if only for sake of curiosity. I
alrteady refactored my code and have now two classes, one const and
one non const.
Cheers
Fabio
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Fabio
|
6/22/2009 9:48:47 AM
|
|
Edward Rosten wrote:
> On 19 June, 17:47, SG <s.gesem...@gmail.com> wrote:
>
>>> These may be the same:
>>> typedef int * B
>>> const B <=> int * const
>>> These are not:
>>> void foo(const double* d);
>>> void foo(const ArraySilce& s);
>> Of course not. This comparison is nonsense. See below.
>
> No it is not nonsense. You keep telling me what the rules of C++ are.
> However one still writes:
>
> void foo(const double* d);
>
> and one can still not write this:
>
> void foo(const ArraySilce& s);
>
>
> The fact that ArraySlice may be a typedef double* is besides the
> point. The point is that the first function is not equivalent to the
> second. We agree on this. It also means that you can not get user
> defined types to behave like builtin ones, because there is no way of
> making the second one behave like the first. The fact that you can
> make builtin types behave like user defined one is entirely besides
> the point. Consider this: constructing builtin arrays has a nice
> syntax. Constructing user-defined array like objects does not. The way
> of fixing this is to introduce sequence constructors.
Which is what WG21 have done for C++0x
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
6/23/2009 12:11:00 PM
|
|
Edward Rosten wrote:
> On 19 June, 17:47, SG <s.gesem...@gmail.com> wrote:
[snip]
>> You can parameterize the type of elements your ArraySlice points to to
>> avoid code duplication:
>>
>> void foo(const double* d);
>> void foo(ArraySilce<const couble> s);
>>
>> would be the equivalent. (!)
>
> I know that. But if you have a corresponding struct, eg Array with
> copy semantics you would have to write instead:
>
> void foo(const Array<double>&);
>
> Look at the following two functions:
>
> void foo(const Array<double>&);
> void foo(ArraySilce<const couble> s);
>
> Does that not look horribly inconsistent to you? It does to me.
Note that some prefer a more... hm... mathematical (?) style of writing:
void foo(Array<double> const & a)
void bar(ArraySlice<double> mutable_slice);
void bar(ArraySlice<double const> const_slice);
void bar(double * mutable_slice);
void bar(double const * const_slice);
It seems consistent to me...
I also seems that your issues are _not_ with builtin pointers
vs custom pointers, but with Array<double> vs double *.
Please consider that Array<double> is _not_nearly_ the same as double *.
On copy, Array it will copy all it's elements, where double * will not
(ownership issues); Array propagates const, where double * doesn't
(this is by choice, it doesn't need to be!). These differences
are intentional, and the two shouldn't be compared with respect to
semantics, only their use cases.
If you design Array2 which copies elements, doesn't propagate const
(you have to use Array2<double const> for this), and you have
the consistencies you would like to have. As for me, I can
work with both classes without raising questions.
OTOH, pointers behave, and _should_ always behave the same,
whether built-in or custom. ArraySlice is a pointer-like
class and this is because of the way it should behave
on copy and assignment and because of element ownership.
And please, if you can define consistent logic and semantics
from scratch, not related to C++, I'm sure many of us would
like to see. Because IMHO, these issues are purely mathematic
and logic, and have nothing to do with C++ syntax.
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/23/2009 12:12:02 PM
|
|
Fabio wrote:
>> aggregate<int const> a(p) ;
>
> Ok, I misunderstood the example. My silly.
>
> So we have
> aggregate<int const>
> and
> aggregate<int>
>
> However, how do we define a constructor from aggregate<int> to
> aggregate<const int>?
> I would like to be able to initialize a const view from a non const
> one.
Both SG and I have provided examples and Mathias Gaunard provided fixes.
Less talk, more read!
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/23/2009 12:12:20 PM
|
|
On 22 Jun., 18:35, Edward Rosten wrote:
> On 19 June, 17:47, SG wrote:
> > Edward Rosten wrote:
>
> > > These may be the same:
>
> > > typedef int * B
> > > const B <=> int * const
>
> > > These are not:
>
> > > void foo(const double* d);
> > > void foo(const ArraySilce& s);
>
> > Of course not. This comparison is nonsense. See below.
>
> No it is not nonsense. You keep telling me what the rules
> of C++ are.
IIRC it was the first time I addressed you in this thread. Anyhow, I
did that because it looks me that you confuse something. And it still
does.
> However one still writes:
>
> void foo(const double* d);
>
> and one can still not write this:
>
> void foo(const ArraySilce& s);
Sorry, did you actually show the ArraySlice implementation somewhere?
What is it supposed to do? Is it supposed to act like a pointer?
Because that's what we're talking about, right?
I really don't see the problem with user-defined pointer-like types
(or any inconsistency compared to builtin pointers). Can you
elaborate?
> The point is that the first function is not equivalent to the
> second. We agree on this. It also means that you can not get user
> defined types to behave like builtin ones, because there is no
> way of making the second one behave like the first.
What second type and what first type exactly?
Of course I can define ArraySlice to behave like double*. And I can
define ConstArraySlice to behave like double const*. So, tell me,
where's the inconsistency? double const* and double* are /also/ two
different types. The first is a pointer to const double and the second
is a pointer to non-const double.
> > You can parameterize the type of elements your ArraySlice points to to
> > avoid code duplication:
>
> > void foo(const double* d);
> > void foo(ArraySilce<const couble> s);
>
> > would be the equivalent. (!)
>
> I know that. But if you have a corresponding struct, eg Array with
> copy semantics you would have to write instead:
>
> void foo(const Array<double>&);
>
> Look at the following two functions:
>
> void foo(const Array<double>&);
> void foo(ArraySilce<const couble> s);
>
> Does that not look horribly inconsistent to you? It does to me.
Please explain how this example shows that there is an inconsistency
between builtin pointer types and a class template like ArraySlice. It
seems you havn't yet decided about the ArraySlice type's semantics
(pointer or reference?).
> My argument is that you get a horrible inconsistency with user-defined
> types
Where is this inconsistency?
> that you do not get with built-in ones (pointers). Repeatedly
> telling me that I can't get what I want within the rules of C++ is not
> a counter argument.
I'm not saying that you can't get what you want. I don't even know
what that would be exactly.
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SG
|
6/23/2009 12:14:41 PM
|
|
On Jun 22, 12:48 pm, Fabio <fabio.canni...@gmail.com> wrote:
> > aggregate<int const> a(p) ;
>
> Ok, I misunderstood the example. My silly.
>
> So we have
> aggregate<int const>
> and
> aggregate<int>
>
> However, how do we define a constructor from aggregate<int> to
> aggregate<const int>?
> I would like to be able to initialize a const view from a non const
> one.
One way is to provide a conversion operator from aggregate<T> to
aggregate<T const> for example:
template<class T>
class aggregate
{
T * p_ ;
public :
...
operator aggregate<T const> ( ) { return aggregate<T const>(p_) ; }
...
} ;
However, I don't know the pitfalls with this approach. Hopefully
others can point you to better approaches and or examples. Boost
probably has some libraries worth checking out such as the smart
pointer and range libraries.
> I posted a detailed exanation of what I am trying to do early in this
> post. Anyway, my question now if only for sake of curiosity. I
> alrteady refactored my code and have now two classes, one const and
> one non const.
Ok. I hope it works out nicely.
KHD
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Keith
|
6/23/2009 12:59:20 PM
|
|
On 23 June, 20:14, SG <s.gesem...@gmail.com> wrote:
> IIRC it was the first time I addressed you in this thread. Anyhow, I
> did that because it looks me that you confuse something. And it still
> does.
My apologies. I noticed I had misread the order of the threads. I can
still assure you, however that I am not confused.
> > However one still writes:
>
> > void foo(const double* d);
>
> > and one can still not write this:
>
> > void foo(const ArraySilce& s);
>
> Sorry, did you actually show the ArraySlice implementation somewhere?
> What is it supposed to do? Is it supposed to act like a pointer?
> Because that's what we're talking about, right?
It's some slice object. For instance it could hold a pointer and a
size. It would allow you to act on a chunk of an array just like an
array.
> I really don't see the problem with user-defined pointer-like types
> (or any inconsistency compared to builtin pointers). Can you
> elaborate?
Yes:
double* //This type can be used as a slice of an array, or a
whole array
const double* //This type can be used as an immutable slice of an
array or a whole immutable array.
Of course, those types require more by-hand book keeping but that's
what one gets for using C or C-like constructs.
Let us say that there are now some types which act like arrays. They
overload[] and have a .size() member:
Array<double>
ArraySlice<double>
one holds an array with copy semantics. The other holds a slice of an
array. A reasonable design might allow you to slice an array, so if
you have:
Array<double> a;
....
a.slice(0,5); //This returns an ArraySlice object, starting at a[0],
ending at a[4].
Let us say you have a function which operates on immutable data. To
take an array you would write:
double sum(const Array<double>& s); //Sum up the contents
whereas to take a slice you have to write:
double sum(ArraySlice<const double>& s);
Or maybe, if there is a ConstArraySlice class which holds a const
whatever*:
double sum(ConstArraySlice<double>& s);
So, Array<double> and ArraySlice<double> look pretty much the same
from the outside. They both have operator[] and .size(). sum is quite
obvious and does no copying of the data. Yet despite these
similarities, const Array<> and const ArraySlice<> behave very
differently. This seems inconsistent. Whereas, if you write it C-
style, you just need:
double sum(const double*, int size);
which is one function rather than two. To be able to write one
function in C++, consider you the following class:
template<class T, template<class> class Base> class Array: public
Base<T>;
Now, the behavior of Array depends on it's base. One can write a Base
(called eg called Alloc) which holds an array with copy semantics
(like std::vector) or one can write a Base (eg called Slice) which
holds a pointer and just copies the pointer. This way, the base class
can make Array behave like an Array or a slice.
Now, you can write a single function like this:
template<class T, template<class> class B> T sum(const Array<T, B>&);
We're now rather closer to the nice properties of the C-style code.
For instance, there is now one function where instead there were two.
You can write functions which take an Array to represent a chunk of
data either const or not const, and you don't have to care whether
you're operating on an array or a slice. That's good. It's like the C-
style function, except much nicer.
Except now I've made a nice little hole in the type system, since a
const double* is not the same as a double const* (ie an const
Array<double, Slice>& is not the same as an Array<const double,
Slice>&). Of course, one has to jump through some slightly silly hoops
to get at the hole, but nevertheless, it's there.
So, in a nutshell, here's my complaint. I can write a single C-style
function which operates on immutable data, regardless of whether it's
an array or merely a slice of an array, because pointers are rather
dumb and also const binds differently for *. I can not do the same for
user defined types without making a small hole in the type system.
This is not a contrived example. It is a very simplified version of
the internals of this numerics library:
http://svr-www.eng.cam.ac.uk/~er258/cvd/toon.html
Please feel to comment on the library if you wish. I can not think of
a better way of achieving the goals I want.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/25/2009 3:38:27 PM
|
|
On 26 Jun., 00:38, Edward Rosten <Edward.Ros...@gmail.com> wrote:
> On 23 June, 20:14, SG <s.gesem...@gmail.com> wrote:
>
> > Sorry, did you actually show the ArraySlice implementation somewhere?
> > What is it supposed to do? Is it supposed to act like a pointer?
> > Because that's what we're talking about, right?
>
> It's some slice object. For instance it could hold a pointer and a
> size. It would allow you to act on a chunk of an array just like an
> array.
What does "It's some slice object" mean? It it supposed to have
reference semantics or pointer semantics? You can implement both with
a pointer but one propagates const and forwards assignments to the
slice and the other behaves like a pointer (not propagating const and
assignment doesn't change the current slice's elements but changes
what "slice" is being pointed to).
> > I really don't see the problem with user-defined pointer-like types
> > (or any inconsistency compared to builtin pointers). Can you
> > elaborate?
>
> Yes:
> double* // This type can be used as a slice of an array, or
> // a whole array
No, it's always a pointer -- no value semantics w.r.t. the elements.
> const double* // This type can be used as an immutable slice of
> // an array or a whole immutable array.
>
> Of course, those types require more by-hand book keeping but that's
> what one gets for using C or C-like constructs.
>
> Let us say that there are now some types which act like arrays.
How do "arrays" act in your world? Are they copiable (like
tr1::array<>)?
> They
> overload[] and have a .size() member:
> Array<double>
> ArraySlice<double>
Ok, so anything that has these member function "acts like an array". I
think this is a big part of your design problem. You're mixing
different concepts just because they have the same syntax.
> one holds an array with copy semantics. The other holds a slice of an
> array.
Yes. Whatever "slice" means. (pointer or reference semantics?)
> A reasonable design might allow you to slice an array, so if
> you have:
> Array<double> a;
> ...
> a.slice(0,5); //This returns an ArraySlice object, starting at a[0],
> ending at a[4].
>
> Let us say you have a function which operates on immutable data. To
> take an array you would write:
>
> double sum(const Array<double>& s); //Sum up the contents
>
> whereas to take a slice you have to write:
> double sum(ArraySlice<const double>& s);
I would not know because I don't know how "slice" is going to behave.
It's odd, though, that you pass a slice by reference to non-const.
> Or maybe, if there is a ConstArraySlice class which holds a const
> whatever*:
> double sum(ConstArraySlice<double>& s);
Again, reference to non-const.... Hmmm....
> So, Array<double> and ArraySlice<double> look pretty much the same
> from the outside. They both have operator[] and .size().
same syntax, yes, but /different/ concept.
> sum is quite
> obvious and does no copying of the data. Yet despite these
> similarities, const Array<> and const ArraySlice<> behave very
> differently. This seems inconsistent.
By "differently" you mean one has value semantics and the other has
reference or pointer semantics? Well, that was intentional, wasn't it?
It's the point of having a class for slices, right?
> Whereas, if you write it C-
> style, you just need:
>
> double sum(const double*, int size);
>
> which is one function rather than two.
This function always takes a pointer. You could design ArraySlice to
behave like a pointer an add an implicit convertion from Array to
ArraySlice if you like. Then, you'd be really emulating the behaviour
of the built-in types (array, pointer) with additional benefit of
carrying "size" around. Though, you probably should not make this
conversion implicit. It could be error-prone. For an explicit
conversion you could use little helper functions like these:
template<typename T>
inline ArraySlice<T> slice(Array<T>&);
template<typename T>
inline ArraySlice<const T> cslice(const Array<T>&);
(assuming ArraySlice<T> behaves like a pointer)
> To be able to write one
> function in C++, consider you the following class:
> template<class T, template<class> class Base> class Array: public
> Base<T>;
>
> Now, the behavior of Array depends on it's base. One can write a Base
> (called eg called Alloc) which holds an array with copy semantics
> (like std::vector) or one can write a Base (eg called Slice) which
> holds a pointer and just copies the pointer. This way, the base class
> can make Array behave like an Array or a slice.
>
> Now, you can write a single function like this:
>
> template<class T, template<class> class B> T sum(const Array<T, B>&);
I think this is a bad design idea. You should not let the behaviour of
an class instantiation depend on a template parameter like this.
Array<T,B> will have value semantics for some templates B and poitner
or reference semantics for other templates B. This ought to confuse
people.
> We're now rather closer to the nice properties of the C-style code.
Not really.
> For instance, there is now one function where instead there were two.
You, you still have multiple functions because you declared a function
template. ;-)
> You can write functions which take an Array to represent a chunk of
> data either const or not const, and you don't have to care whether
> you're operating on an array or a slice. That's good. It's like the C-
> style function, except much nicer.
I don't agree. It's not "much nicer".
> Except now I've made a nice little hole in the type system, since a
> const double* is not the same as a double const* (ie an const
It is. const double* and double const* are the SAME types. As for
"little hole in the type system": Your design is to blame.
> So, in a nutshell, here's my complaint. I can write a single C-style
> function which operates on immutable data, regardless of whether it's
> an array or merely a slice of an array, because pointers are rather
> dumb and also const binds differently for *.
How can you say you're not confused and yet say something like "const
binds differently for *"? It sounds like you're implying that there is
some inconsistency. But there is none. You just seem to confuse
concepts and don't like the pointer declaration syntax.
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SG
|
6/26/2009 12:02:02 PM
|
|
Edward Rosten wrote:
> So, in a nutshell, here's my complaint. I can write a single C-style
> function which operates on immutable data, regardless of whether it's
> an array or merely a slice of an array, because pointers are rather
> dumb and also const binds differently for *. I can not do the same for
> user defined types without making a small hole in the type system.
Const binds always the same, and no need for drilling holes.
Did you read my other post? Because you still speak about
"custom pointers vs built-in pointers" when in fact it is
about "custom arrays vs built-in (evil) arrays"...
Anyway, access to an array is only one part of its interface.
The rest of it is element ownership, copy/assignment.
If you ignore these, there is no need to use anything beside
double * anyway.
Your wish is for a single interface for both classes. This is
how it can be done (names suck though, and this is only
a non-template example)...
class ArrayAccessInterface {
public:
virtual ~ArrayAccessInterface();
virtual double operator[](int index) const = 0;
};
class ArrayAccess : public ArrayAccessInterface {
public:
explicit ArrayAccess(Array<double> const & a)
: m_a(&a) {}
virtual double operator[](int index) const {
return (*m_a)[index];
}
private:
Array<double> const * m_a;
};
class ArraySliceAccess : public ArrayAccessInterface {
public:
explicit ArraySliceAccess(ArraySlice<const double> const & a)
: m_a(&a) {}
virtual double operator[](int index) const {
return (*m_a)[index];
}
private:
ArraySlice<const double> const * m_a;
};
void func(ArrayAccessInterface const & a) { /* bla a[0] bla */ }
Array<double> p; func(ArrayAccess(p));
ArraySlice<const double> q; func(ArraySliceAccess(q));
If you don't like this method, you can write template functions
that will take anything that has operator[] that returns double.
With incoming concepts it can be very clean:
template <typename A>
/* C++0x only */ requires MyCustomArrayOfDoubleConcept<A>
void func(A const & a) { /* bla a[0] bla */ }
Array<double> p; func(p);
ArraySlice<const double> q; func(q);
Now... either choose one of these solutions, or please do as I asked
in the mentioned other post: "... define consistent logic and
semantics from scratch, not related to C++ ..." So make a fictional
syntax/language and define relevant classes and their behavior
the way you would want them to work.
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/26/2009 12:12:16 PM
|
|
On 26 June, 20:02, SG <s.gesem...@gmail.com> wrote:
> What does "It's some slice object" mean? It it supposed to have
> reference semantics or pointer semantics? You can implement both with
> a pointer but one propagates const and forwards assignments to the
> slice and the other behaves like a pointer (not propagating const and
> assignment doesn't change the current slice's elements but changes
> what "slice" is being pointed to).
Essentially a slice of an array refers to sum subset of the array. See
eg:
http://en.wikipedia.org/wiki/Array_slicing
> > Yes:
> > double* // This type can be used as a slice of an array, or
> > // a whole array
>
> No, it's always a pointer -- no value semantics w.r.t. the elements.
Yes, so the const double* can refer to the whole array or a slice of
it. The only semantice it has wrt the elements is that they exist.
> > Let us say that there are now some types which act like arrays.
>
> How do "arrays" act in your world? Are they copiable (like
> tr1::array<>)?
Yes, that seems reasonable.
> > They
> > overload[] and have a .size() member:
> > Array<double>
> > ArraySlice<double>
>
> Ok, so anything that has these member function "acts like an array". I
> think this is a big part of your design problem. You're mixing
> different concepts just because they have the same syntax.
That's half the point of C++ and templates, and ideed duck typing in
general. A lot of the STL works this way: if you can provide
iterators, it does not matter what the underlying concept is.
In my case, if you provide [] and .size() it does not matter much
whether you are referring to data that you own or data that someone
else owns.
> > one holds an array with copy semantics. The other holds a slice of an
> > array.
>
> Yes. Whatever "slice" means. (pointer or reference semantics?)
Could be either. For simplicity lets make it reference semantics: you
can't change the data a slice points to.
> > double sum(const Array<double>& s); //Sum up the contents
>
> > whereas to take a slice you have to write:
> > double sum(ArraySlice<const double>& s);
>
> I would not know because I don't know how "slice" is going to behave.
Well, slicing is not an uncommon concept in computer science.
> It's odd, though, that you pass a slice by reference to non-const.
Posted code need not compile :-) I think I was missing a leading
const.
> > So, Array<double> and ArraySlice<double> look pretty much the same
> > from the outside. They both have operator[] and .size().
>
> same syntax, yes, but /different/ concept.
Really? The concept is the same. Both provide array-like access to a
bunch of data.
> By "differently" you mean one has value semantics and the other has
> reference or pointer semantics? Well, that was intentional, wasn't it?
> It's the point of having a class for slices, right?
How else do you do slices?
> > Whereas, if you write it C-
> > style, you just need:
>
> > double sum(const double*, int size);
>
> > which is one function rather than two.
>
> This function always takes a pointer. You could design ArraySlice to
> behave like a pointer an add an implicit convertion from Array to
> ArraySlice if you like. Then, you'd be really emulating the behaviour
> of the built-in types (array, pointer) with additional benefit of
> carrying "size" around. Though, you probably should not make this
> conversion implicit. It could be error-prone. For an explicit
> conversion you could use little helper functions like these:
>
> template<typename T>
> inline ArraySlice<T> slice(Array<T>&);
> template<typename T>
> inline ArraySlice<const T> cslice(const Array<T>&);
>
> (assuming ArraySlice<T> behaves like a pointer)
One could but one then has the rather nasty thing that 1. you
instantiate Array objects yet write functions to accept ArraySlice
objecgts and 2 have to duplicate all the internals of Array in
ArraySlice, such as operator[], slicing, and so on.
> > template<class T, template<class> class B> T sum(const Array<T, B>&);
>
> I think this is a bad design idea. You should not let the behaviour of
> an class instantiation depend on a template parameter like this.
Why not?
> Array<T,B> will have value semantics for some templates B and poitner
> or reference semantics for other templates B. This ought to confuse
> people.
Yes, it changes its behaviour. In my experience people prefer this to
having two different types. Arrays are instantiated, and Arrays are
accepted by functions. It's a very uniform way of doing things.
> > We're now rather closer to the nice properties of the C-style code.
>
> Not really.
Yes we are. You only have to write one function, like in C. One
function versus two means one source of bugs versus two.
> > For instance, there is now one function where instead there were two.
>
> You, you still have multiple functions because you declared a function
> template. ;-)
But this is an essential point. You only have to write/fix/debug/
update one function. Code duplication is generally aa bad thing.
> > Except now I've made a nice little hole in the type system, since a
> > const double* is not the same as a double const* (ie an const
>
> It is. const double* and double const* are the SAME types. As for
> "little hole in the type system": Your design is to blame.
Typo. const double * != double * const.
Or, more to the point:
const foo != const double* where foo==double*
> > So, in a nutshell, here's my complaint. I can write a single C-style
> > function which operates on immutable data, regardless of whether it's
> > an array or merely a slice of an array, because pointers are rather
> > dumb and also const binds differently for *.
>
> How can you say you're not confused and yet say something like "const
> binds differently for *"? It sounds like you're implying that there is
because const foo != const double* where foo = double*
> some inconsistency. But there is none. You just seem to confuse
> concepts and don't like the pointer declaration syntax.
I still claim that it is not possible to write an array and array
slice system without either duplicating functions, duplicating classes
or making a hole in the type system.
-Ed
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/28/2009 1:34:33 PM
|
|
On 26 June, 20:12, Dragan Milenkovic <dra...@plusplus.rs> wrote:
> > dumb and also const binds differently for *. I can not do the same for
> > user defined types without making a small hole in the type system.
>
> Const binds always the same, and no need for drilling holes.
My point is:
const double* != const foo where foo==double*
which is mostly what one wants.
> Did you read my other post? Because you still speak about
> "custom pointers vs built-in pointers" when in fact it is
> about "custom arrays vs built-in (evil) arrays"...
>
> Anyway, access to an array is only one part of its interface.
> The rest of it is element ownership, copy/assignment.
> If you ignore these, there is no need to use anything beside
> double * anyway.
Really? Because std::accumulate, std::sort and etc precisely ignore
all of these things. I am essentially talking about the same thing:
providing algorithms on one part of the interface while ignoring other
parts.
> Your wish is for a single interface for both classes. This is
> how it can be done (names suck though, and this is only
> a non-template example)...
>
> class ArrayAccessInterface {
> public:
> virtual ~ArrayAccessInterface();
>
> virtual double operator[](int index) const = 0;
I didn't mention this before, but I can't afford virtual functions for
things like operator[]. They are much too slow.
> If you don't like this method, you can write template functions
> that will take anything that has operator[] that returns double.
> With incoming concepts it can be very clean:
>
> template <typename A>
> /* C++0x only */ requires MyCustomArrayOfDoubleConcept<A>
> void func(A const & a) { /* bla a[0] bla */ }
>
> Array<double> p; func(p);
> ArraySlice<const double> q; func(q);
That would work for many situations, however IMO is is not quite
restrictive enough. In my library operator* on Vector does dot
products. On other libraries it does element by element multiplication
on Arrays. Both are reasonable choices, and both unfortunately have
the same interface. Accepting a generic type would cause problems. I
suppose one could add a specific tag to Vector to allow it to be
detected. This is so far the only sensible system that's been
suggested.
But I still think it's a shame that one can make a foo behave like
double* but not make const foo behave like const double*.
> Now... either choose one of these solutions, or please do as I asked
> in the mentioned other post: "... define consistent logic and
> semantics from scratch, not related to C++ ..." So make a fictional
> syntax/language and define relevant classes and their behavior
> the way you would want them to work.
Why would I want it unrelated to C++? I'm not going to switch to a
hypothetical language.
-Ed
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/28/2009 1:34:59 PM
|
|
Edward Rosten wrote:
> On 26 June, 20:12, Dragan Milenkovic <dra...@plusplus.rs> wrote:
>
>>> dumb and also const binds differently for *. I can not do the same for
>>> user defined types without making a small hole in the type system.
>> Const binds always the same, and no need for drilling holes.
>
> My point is:
>
> const double* != const foo where foo==double*
>
> which is mostly what one wants.
>
>> Did you read my other post? Because you still speak about
>> "custom pointers vs built-in pointers" when in fact it is
>> about "custom arrays vs built-in (evil) arrays"...
>>
>> Anyway, access to an array is only one part of its interface.
>> The rest of it is element ownership, copy/assignment.
>> If you ignore these, there is no need to use anything beside
>> double * anyway.
>
> Really? Because std::accumulate, std::sort and etc precisely ignore
> all of these things. I am essentially talking about the same thing:
> providing algorithms on one part of the interface while ignoring other
> parts.
And the next design covers this. I learned it from Bjarne's book.
>> Your wish is for a single interface for both classes. This is
>> how it can be done (names suck though, and this is only
>> a non-template example)...
>>
>> class ArrayAccessInterface {
>> public:
>> virtual ~ArrayAccessInterface();
>>
>> virtual double operator[](int index) const = 0;
>
> I didn't mention this before, but I can't afford virtual functions for
> things like operator[]. They are much too slow.
And the next design covers this.
>> If you don't like this method, you can write template functions
>> that will take anything that has operator[] that returns double.
>> With incoming concepts it can be very clean:
>>
>> template <typename A>
>> /* C++0x only */ requires MyCustomArrayOfDoubleConcept<A>
>> void func(A const & a) { /* bla a[0] bla */ }
>>
>> Array<double> p; func(p);
>> ArraySlice<const double> q; func(q);
>
> That would work for many situations, however IMO is is not quite
> restrictive enough. In my library operator* on Vector does dot
> products. On other libraries it does element by element multiplication
> on Arrays. Both are reasonable choices, and both unfortunately have
> the same interface. Accepting a generic type would cause problems. I
> suppose one could add a specific tag to Vector to allow it to be
> detected. This is so far the only sensible system that's been
> suggested.
I fail to see the relevance of "operator*". The only thing that
concept MyCustomArrayOfDoubleConcept requires is operator[],
which is exactly the interface you would want. You can restrict
a concept any way you like.
> But I still think it's a shame that one can make a foo behave like
> double* but not make const foo behave like const double*.
We have irreconcilable differences, and I'm filing for a divorce.
Maybe in time you will come to your senses and we could remain friends.
:-D
--
Dragan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Dragan
|
6/28/2009 5:50:29 PM
|
|
On 29 June, 01:50, Dragan Milenkovic <dra...@plusplus.rs> wrote:
> > That would work for many situations, however IMO is is not quite
> > restrictive enough. In my library operator* on Vector does dot
> > products. On other libraries it does element by element multiplication
> > on Arrays. Both are reasonable choices, and both unfortunately have
> > the same interface. Accepting a generic type would cause problems. I
> > suppose one could add a specific tag to Vector to allow it to be
> > detected. This is so far the only sensible system that's been
> > suggested.
>
> I fail to see the relevance of "operator*". The only thing that
> concept MyCustomArrayOfDoubleConcept requires is operator[],
> which is exactly the interface you would want. You can restrict
> a concept any way you like.
This is the problem with examples: one inevitable forgets to include
some parts. I'm describing a limited version of the system I posted a
URL for earlier in the thread. The trouble with the concepts approach
is that it only works on the interface. You can have two different
classes with *exactly* the same interface. For instance std::valarray
and TooN::Vector (the library I refer to earlier) have the same
interface. Both provide operator*, operator[] and .size() for
instance. std::valarray does element-wise multiplication, TooN::Vector
returns the inner product.
Also, if I provide a generic operator* using the above concept code it
will attempt to work on a variety of things, many of which might not
make semantic sense. It will also happily gobble up std::array,
std::vector and so on.
It is likely I have missed something, though. I haven't taken the time
to really work with concepts yet.
> > But I still think it's a shame that one can make a foo behave like
> > double* but not make const foo behave like const double*.
>
> We have irreconcilable differences, and I'm filing for a divorce.
> Maybe in time you will come to your senses and we could remain friends.
> :-D
Actually, I think this has been rather fruitful. I didn't consider the
concepts approach until now. I really am liiking for a real solution
to a problem I have.
-Ed
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Edward
|
6/29/2009 10:37:28 AM
|
|
On 28 Jun., 22:34, Edward Rosten <Edward.Ros...@gmail.com> wrote:
> http://en.wikipedia.org/wiki/Array_slicing
"Depending on the programming language and context, the elements
of the new array may be aliased to (i.e., share memory with)
those of the original array."
> Essentially a slice of an array refers to sum subset of the array.
This is not very specific w.r.t. copy/assignment semantics and how
const-qualification affects the result of operator[].
> > You're mixing
> > different concepts just because they have the same syntax.
> [...]
> That's half the point of C++ and templates, and ideed duck typing in
> general. A lot of the STL works this way: if you can provide
> iterators, it does not matter what the underlying concept is.
The concept is always the same: It's an iterator. An iterator behaves
like a pointer. You can safely copy an iterator without the need to
worry about what's gonna happen to the elements it refers to. If I
have a const reference to an iterator I know I may be able to modify
the object it points to because it's just a top-level const. You're
forgetting that iterators are an additional abstraction layer between
containers and algorithms.
You want algorithms to run on anything that has .operator[]() and .size
(), containers and (shallow) container views. For this you write a
funtion to take an "array-like" object by reference. It's all fine and
dandy as long as you don't accitentally try to copy such an object
while having kind of copy semantics in mind that doesn't match the
object's.
> > > double sum(const Array<double>& s); //Sum up the contents
>
> > > whereas to take a slice you have to write:
> > > double sum(ArraySlice<const double>& s);
>
> > I would not know because I don't know how "slice" is going to behave.
>
> Well, slicing is not an uncommon concept in computer science.
But you left out the details of concepts. I can think of at least two
"slice" classes which I referred to by "pointer semantics" and
"reference semantics". For the first version I would declare the sum
function like this:
double sum(ArraySlice<const double> s); //<-- pass by value
In this case ArraySlice models a "view" into an array. A copy of
ArraySlice will refer to the same elements. Assignment of ArraySlice
objects doesn't change the elements but changes what elements the
slice object refers to (like a pointer). By creating ArraySlice
objects that refer to all elements of an array you can reuse this
function. This is what happens with the array-to-pointer decay in C
and C++. You can't get any closer semantically to that than this. In C+
+0x you could add an implicit conversion from Array<T> to
ArraySlice<T> and ArraySlice<const T> in case the Array<T> object was
an lvalue (your own "array-to-slice" decay if you will).
The second slice class version acts like a reference. Once initialized
it cannot be reseated. It refers to a subrange of an array and
propagates const to its elements. It would allow you to write a single
function template that can be used with a slice object as well as an
array object in case NO copying is done:
template<typename NonCopiableArrayLike>
double sum(NonCopiableArrayLike const& s);
NonCopiableArrayLike can be an Array<double>, or an ArraySlice<double>
or an ArraySlice<const double>. Either way, s is const-qualified and
since ArraySlice<double> propagates const to its elements you get a
consistent behaviour compared to with Array<double>. This is NOT what
a pointer would do. I used the name "NonCopiableArrayLike"
deliberately because as long as this "thing" is not copied we wouldn't
know the difference between an array with value semantics and a slice
with reference semantics. But they ARE models of different concepts if
you also count copy semantics.
> > > So, Array<double> and ArraySlice<double> look pretty much the same
> > > from the outside. They both have operator[] and .size().
>
> > same syntax, yes, but /different/ concept.
>
> Really? The concept is the same. Both provide array-like access to a
> bunch of data.
So, your "ArrayLike"-concept doesn't say anything about copy/
assignment semantics.
> How else do you do slices?
I generally prefer to distinguish between a container ("store" with
value semantics) and something that only refers to elements (like a
pointer) because I prefer to know what happens if I copy such an
object and how a const qualification affects the result of operator[].
For me, this is part of the concept (C++0x terminology "concept").
> > [on an ArraySlice<T> version behaving like a pointer to T with
> > known size]
>
> One could but one then has the rather nasty thing that 1. you
> instantiate Array objects yet write functions to accept ArraySlice
> objecgts
But that would be equivalent to taking a T* and an int for the size as
parameters. If I remember correctly, you mentioned that it's
impossible to write user-defined types that emulate this behaviour.
Well, you're wrong, obviously.
> > I think this is a bad design idea. You should not let the behaviour of
> > an class instantiation depend on a template parameter like this.
>
> Why not?
I think it's error-prone to let the copy/assignement semantics of a
class be determined by a template parameter while declaring functions
like these:
template<typename T, typename B>
void do_something(Array<T,B> const& x)
{
// can I copy x? What will happen? Well, it
// depends on B, doesn't it?
}
> > > Except now I've made a nice little hole in the type system, since
> > [...]
> Typo. const double * != double * const.
> Or, more to the point:
> const foo != const double* where foo==double*
It's not obvious to me how this is a bad thing. On the contrary, it's
consistent. I really have trouble figuring out whether you just don't
like the pointer declaration syntax or you don't like the fact that
top-level const for builtin pointers (intentionally) doesn't propagate
down to pointees. It's certainly not a hole in the type system. If
anything, your design is broken. If you want double const* then simply
use double const* and not double*const.
> > > So, in a nutshell, here's my complaint. I can write a single C-style
> > > function which operates on immutable data, regardless of whether it's
> > > an array or merely a slice of an array, because pointers are rather
> > > dumb and also const binds differently for *.
>
> > How can you say you're not confused and yet say something like "const
> > binds differently for *"? It sounds like you're implying that there is
>
> because const foo != const double* where foo = double*
We've established that already. It still isn't a coherent argument for
your claim/complaint. All you do is giving the impression that you're
confusing something (pointer declaration syntax and/or the difference
between arrays and pointers and/or the implicit array-to-pointer decay
and/or how a const qualified pointer behaves).
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
SG
|
6/29/2009 11:02:50 AM
|
|
|
60 Replies
521 Views
(page loaded in 0.559 seconds)
|