Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

std::vector of const values

0 views
Skip to first unread message

Dave

unread,
Nov 21, 2009, 11:52:27 PM11/21/09
to
Hi all,

I have run into this problem a few times and was hoping there was a
more elegant solution than what I was doing. Suppose I have the
following code:

#include <vector>
#include <iostream>

void doStuff( const std::vector< const int* >& ints )
{
for( size_t i = 0; i != ints.size(); ++i )
std::cout << *ints[ i ] << std::endl;
}

int main()
{
int i1, i2, i3;
std::vector< int* > ints;
ints.push_back( &i1 );
ints.push_back( &i2 );
ints.push_back( &i3 );
doStuff( ints );
return 0;
}


Now, if I try to compile this (with gcc 4.3.2, though I get similar
problems with MSVC++ 2003), I get the following error:

constvector.cc: In function 'int main()':
constvector.cc:19: error: invalid initialization of reference of type
'const std::vector<const int*, std::allocator<const int*> >&' from
expression of type 'std::vector<int*, std::allocator<int*> >'
constvector.cc:5: error: in passing argument 1 of 'void doStuff(const
std::vector<const int*, std::allocator<const int*> >&)'

Which is really just saying that the parameter to doStuff() is vector<
const int* > but the argument I am passing it is vector< int* >
(without the const). To get around this, I have to create a copy of
the vector with the right template param type and then pass that to my
function.

However, it seems to me that, if the compiler wanted to, it could just
treat the argument as vector< const int* > and just proceed. So does
anyone know of a way to do that? Or am I stuck with making copies. Is
there some fundamental reason why a vector of non-const values could
not be treated as a vector of const values?

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Nick Hounsome

unread,
Nov 22, 2009, 8:16:37 AM11/22/09
to

consider

void doStuff( std::vector< const int* >& ints )

This would be able to add const int* to what is actually
std::vector<int*>

Of course since it is actually passed as an immutable vector that
cannot happen but I think that that is getting too sophisticated.

One way to go would be to redefine doStuff as a template method taking
2 iterators and create an iterator adapter.

IMHO it is probably a bad idea to have vector of int* anyway.

Taras Shevchuk

unread,
Nov 22, 2009, 8:14:58 AM11/22/09
to
Dave =D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=B2(=D0=BB=D0=B0):

#include <vector>
> #include <iostream>
>
> void doStuff( const std::vector< const int* >& ints )
> {
> for( size_t i = 0; i != ints.size(); ++i )
> std::cout << *ints[ i ] << std::endl;
> }
>
> int main()
> {
> int i1, i2, i3;
> std::vector< int* > ints;
> ints.push_back( &i1 );
> ints.push_back( &i2 );
> ints.push_back( &i3 );
> doStuff( ints );
> return 0;
> }
>
>

> However, it seems to me that, if the compiler wanted to, it could just
> treat the argument as vector< const int* > and just proceed. So does
> anyone know of a way to do that? Or am I stuck with making copies. Is
> there some fundamental reason why a vector of non-const values could
> not be treated as a vector of const values?
>
>

I propose to use iterators:

template <typename Iter>
void doStuff(Iter begin, Iter end)
{
for(Iter it = begin; it != end; ++it)
{
std::cout << **it << std::endl;
}
}
...
doStuff( ints.begin(), insts.end());

Francis Glassborow

unread,
Nov 22, 2009, 8:15:32 AM11/22/09
to
Dave wrote:

> Hi all,
>
> I have run into this problem a few times and was hoping there was a
> more elegant solution than what I was doing. Suppose I have the
> following code:
>

> #include <vector>
> #include <iostream>
>
> void doStuff( const std::vector< const int* >& ints )
> {
> for( size_t i = 0; i != ints.size(); ++i )
> std::cout << *ints[ i ] << std::endl;
> }
>
> int main()
> {
> int i1, i2, i3;
> std::vector< int* > ints;
> ints.push_back( &i1 );
> ints.push_back( &i2 );
> ints.push_back( &i3 );
> doStuff( ints );
> return 0;
> }
>
>

> Now, if I try to compile this (with gcc 4.3.2, though I get similar
> problems with MSVC++ 2003), I get the following error:
>
> constvector.cc: In function 'int main()':
> constvector.cc:19: error: invalid initialization of reference of type
> 'const std::vector<const int*, std::allocator<const int*> >&' from
> expression of type 'std::vector<int*, std::allocator<int*> >'
> constvector.cc:5: error: in passing argument 1 of 'void doStuff(const
> std::vector<const int*, std::allocator<const int*> >&)'
>
> Which is really just saying that the parameter to doStuff() is vector<
> const int* > but the argument I am passing it is vector< int* >
> (without the const). To get around this, I have to create a copy of
> the vector with the right template param type and then pass that to my
> function.
>

> However, it seems to me that, if the compiler wanted to, it could just
> treat the argument as vector< const int* > and just proceed. So does
> anyone know of a way to do that? Or am I stuck with making copies. Is
> there some fundamental reason why a vector of non-const values could
> not be treated as a vector of const values?
>

> std::vector is a template and that means there is no relationship between
vectors instantiated with different types. int const * and int* are
completely different types. If you want to be able to deal with vectors of
different types write a template function:

template <typename type>
void doStuff(const std::vector<type *> & instances){
for( size_t i(0); i != instances.size(); ++i )
std::cout << *instances[ i ] << std::endl;
}

Now the compiler will generate suitable code for each case (and a really
good optimising compiler might then conflate those that actually have the
same code.

marcin...@gmail.com

unread,
Nov 22, 2009, 8:11:29 AM11/22/09
to
I wouldn't worry about const correctness in this particular case. It
could
be an issue if you would write an interface for other programmer to
implement
and you wouldn't want him to mess data you pass to him. In this case I
can
think of two solutions:

1. Easy way - provide doStuff virtual method for one parameter:

virtual void doStuff(const int& val) = 0;

and non-virtual method for iterating virtual one

typedef std::vector<int*>::const_iterator Iter;


void doStuff(Iter begin, Iter end) {

for(Iter i = begin; i != end; ++i)
std::cout << **i << std::endl;
}

2. Hard (?) way - you could write iterator adapter
(e.g. ConstPtrIter<I> - I for iterator class with value_type T*)
that for iterator with value_type T* would convert it to const T*.
Method in your iterface would look sth like this:

typedef ConstPtrIter<std::vector<int*>::const_iterator> Iter
virtual void doStuff(Iter begin, Iter end) = 0;

However if you are just trying to be const correct at all costs
without a reason, just don't, drop the const from the vector
template parameter and get on with your program ;)

Cheers
Sfider

shahav

unread,
Nov 22, 2009, 11:07:14 AM11/22/09
to
On Nov 22, 6:52 am, Dave <thedaverud...@gmail.com> wrote:
> Hi all,
>
> I have run into this problem a few times and was hoping there was a
> more elegant solution than what I was doing. Suppose I have the
> following code:
>
> #include <vector>
> #include <iostream>
>
> void doStuff( const std::vector< const int* >& ints )
> {
> for( size_t i = 0; i != ints.size(); ++i )
> std::cout << *ints[ i ] << std::endl;
>
> }
>
> int main()
> {
> int i1, i2, i3;
> std::vector< int* > ints;
> ints.push_back( &i1 );
> ints.push_back( &i2 );
> ints.push_back( &i3 );
> doStuff( ints );
> return 0;
>
> }
>
> Now, if I try to compile this (with gcc 4.3.2, though I get similar
> problems with MSVC++ 2003), I get the following error:
>


you should simply add one missing const :


int main()
{
int i1, i2, i3;

std::vector< const int* > ints; // note to the const before int


ints.push_back( &i1 );
ints.push_back( &i2 );
ints.push_back( &i3 );
doStuff( ints );
return 0;

}


Jorgen Grahn

unread,
Nov 24, 2009, 12:31:05 AM11/24/09
to
On Sun, 2009-11-22, Dave wrote:
> Hi all,
>
> I have run into this problem a few times and was hoping there was a
> more elegant solution than what I was doing. Suppose I have the
> following code:
...

> const std::vector< const int* >& ints

...


> Is
> there some fundamental reason why a vector of non-const values could
> not be treated as a vector of const values?

A 'const int*' is not a const value -- it's a non-const pointer to
const int. You can modify it all day long. Not if the vector is
itself const, but I don't think that is what you meant.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Tony Delroy

unread,
Nov 24, 2009, 11:03:50 AM11/24/09
to
On Nov 22, 1:52 pm, Dave <thedaverud...@gmail.com> wrote:
> I have run into this problem a few times and was hoping there was a
> more elegant solution than what I was doing. Suppose I have the
> following [erroneous] code:

>
> void doStuff( const std::vector< const int* >& ints )
> {
> for( size_t i = 0; i != ints.size(); ++i )
> std::cout << *ints[ i ] << std::endl;
> }
>
...
> std::vector< int* > ints;
...
> doStuff( ints );
>
[snip]

>
> However, it seems to me that, if the compiler wanted to, it could just
> treat the argument as vector< const int* > and just proceed. So does
> anyone know of a way to do that? Or am I stuck with making copies.

If you want to trust to your "seems to me", then you can assert this
explicitly to the compiler:

doStuff( *reinterpret_cast<const std::vector<const int*>*>
(&ints));

That will produce undefined behaviour - and therefore isn't useful for
production code - but may suit your purposes. Past that, I'm not
personally interested in comments on the appropriateness of this on clc
++m, but if you use it in shared code you should expect "feedback".

Alternatively, the most direct way to model what you're attempting is
to build "glue" between the type being passed, and the restricted
usage your doStuff function is allowed to make of it:

template <typename T>
struct Vector_Const_Ptr
{
Vector_Const_Ptr(const std::vector<T*>& v)
: ref_(v)
{ }

const T* const operator[](size_t n) const { return ref_[n]; }

size_t size() const { return ref_.size(); }

private:
const std::vector<T*> ref_;
};

void doStuff(const Vector_Const_Ptr<int>& ints)

Though a familiar design pattern I'd say this is also ugly and
confusing, but if you insist on getting that const-ness across, then
this is functional.

Better yet, adopt a different approach as suggested by earlier
replies.

> Is there some fundamental reason why a vector of non-const values could
> not be treated as a vector of const values?

Yes... a template may instantiate code differently for T* and const
T*. For example, the programmer may have used type traits to
differentiate the two at compile time, with alternative code. While
not applicable to the STL, this might be useful: e.g.
- some manner of container of pointers might have a member function to
check for two pointers to the same element,
- given some knowledge of the types the container might hold...
- a non-const version might iterate temporarily marking elements non-
destructively until it sees an already-marked element (result = true)
or reaches the end (result = false),
- it then revisits the earlier elements to remove the mark
- typically faster than building a temporary associative container
to record visited elements.

The compiler has to cope not just with the cleaner less cyclic STL,
but also hackery like the above where the container implementer is
anticipating the container elements and adapting algorithms therefore.

Further - regardless of whether it's practical to do so - the right is
reserved for an optimiser to use the "const"-ness in an attempt to
produce tighter code.

Regards,
Tony

0 new messages