views:

196

answers:

5

I am creating a constructor that will take a pair of input iterators. I want the method signature to have compile-time const semantics similar to:

DataObject::DataObject(const char *begin, const char *end)

However, I can't find any examples of this. For example, my STL implementation's range constructor for vector is defined as:

template<class InputIterator>
vector::vector(InputIterator first, InputIterator last)
{
    construct(first, last, iterator_category(first));
}

which has no compile-time const guarantees. iterator_category / iterator_traits<> contain nothing relating to const, either.

Is there any way to indicate to guarantee the caller that I can't modify the input data?

edit, 2010-02-03 16:35 UTC

As an example of how I would like to use the function, I would like to be able to pass a pair of char * pointers and know, based on the function signature, that the data they point at will not be modified.
I was hoping I could avoid creating a pair of const char * pointers to guarantee const_iterator semantics. I may be forced to pay the template tax in this case.

+1  A: 

What about

#include <vector>

template <class T>
class MyClass{
public:
    MyClass(typename T::const_iterator t1,typename T::const_iterator t2){
    }
    // *EDITED*: overload for pointers (see comments)
    MyClass(const T* t1,const T* t2){
    }
};

void main(){
    std::vector<int> v;
    std::vector<int>::const_iterator it1 = v.begin();
    std::vector<int>::const_iterator it2 = v.end();
    MyClass<std::vector<int> > mv(it1,it2);

    // with pointers:
    char* c1;
    char* c2;
    MyClass mc(c1,c2);
}
Paolo Tedesco
You prevent template parameter deduction by using inner types.
Matthieu M.
Good idea, but I don't have a const_iterator here because I'm passing in two `char *` as iterators.
mskfisher
Add an overload for a pair of pointers.
UncleBens
A: 

That vector constructor is receiving its arguments by value, which means that the caller's iterators are copied before being used in the constructor, which of course means that nothing happens to the caller's iterators.

const for input arguments only really matter when you are passing by reference. e.g.

void foo(int& x)

vs

void foo(const int& x)

In the first example, the caller's input for x may be modified by foo. In the second example, it may not, as the reference is const.

Peter Alexander
Sorry, I wasn't completely clear in my question. I want to ensure that the data that the iterators are pointing at is guaranteed to be treated as const, not that the iterators themselves should be const.
mskfisher
A const_iterator cannot be used to modify the element of the container it's iterating. An ordinary iterator *can* be used that way, even if it's passed by const reference. (Simply make a non-const copy of it.) The types `const iterator` and `const_iterator` aren't the same things.
Rob Kennedy
+8  A: 

The caller can simply use the template with const iterators. If he does, and the compiler doesn't complain, it is guaranteed that the function doesn't modify the data. If it would modify the data, instantiating the template with a const iterator would lead to errors.

You don't really have to force the caller to use const iterators just because you don't modify anything.

sth
+1... and to say I got to all the trouble of indicating how to do it!
Matthieu M.
The semantics of the `DataObject` example mean that I can pass in non-`const` pointers and have them treated as `const` pointers. I don't want to *force* the caller to use `const` iterators - I just want to guarantee that they will only be *used* as `const` iterators.
mskfisher
Either you make defensive copies of the data so it doesn't change (internal const char*), or you document your requirements and move on with life.
KitsuneYMG
That's the thing. I'm trying to get the function signature to both document and defend, just like I can do with `const char *` parameters.
mskfisher
@mskfisher: There is no point to go out of your way over this. For documentation simply call the template parameter ConstIterator if you must, test that the function compiles if given const iterators, and move on.
UncleBens
@UncleBens: I think yours is the right answer, why don't you post it separatedly?
Manuel
I think that is exactly what sth is saying. (And there may be people around here, that would downvote such an answer, which to them appears to say "just ignore const-correctness". :) )
UncleBens
Ups, you're right. sth gets my upvote
Manuel
I think what UncleBen adds, which maybe sth figures goes without saying, is test that the template compiles if given const iterators. If you look at your standard library implementation, you'll probably see that none of the standard algorithms are explicitly checking that they only use capabilities that their template parameters are defined to have, but you can be pretty sure they're tested with "minimally capable" iterators as close as possible. Explicit checked interfaces for templates is what Concepts are for, and Concepts are so difficult to get right that 10 years wasn't long enough ;-).
Steve Jessop
@mskfisher If you require a `const char *`, I can pass in a `char *` and it is promoted to `const char *`. If you require a `char *`, I cannot pass is a `const char *` becuase const can't be implicitly cast away. I believe most of cstring header has `const char *` for source parameters, but the caller can still pass in `char *`
KitsuneYMG
@kts, that's EXACTLY what I'm going for, but I also want to be able to pass in a `vector<char>::iterator`. However, there is no way to const-promote an iterator when passing it in as a template parameter. Specifically, there's no way to generically cast from a `random_access_iterator` to an `input_iterator` (which are the semantics I'm going for in this case).
mskfisher
A: 

This is easy (but not pretty) if you can afford boost:

#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

template<class It>
void f(It b, It e)
{
    using namespace boost;
    typedef typename std::iterator_traits<It>::reference reference;
    BOOST_STATIC_ASSERT(is_const<typename remove_reference<reference>::type>::value);
}

void test()
{
    f((char const*)0, (char const*)0); // Compiles
    f((char*)0, (char*)0);  // Does not compile
}

EDIT: if you want to have and indication about this in your signature then it's common to exploit the name of the template parameter:

template<class ConstIt>
void f(ConstIt b, ConstIt e)
...
Manuel
As I understand the point of the question is only to make sure that the function itself cannot modify the sequence. The point is not to force the user to manually cast things to const iterators.
UncleBens
Like I said in my comment to @sth, I want to require that the iterator can be *used* as a const_iterator, just like a random-access iterator can be used as a forward iterator.
mskfisher
+2  A: 

You could simply create a dummy function which calls your template with char * const pointers. If your template attempts to modify their targets, then your dummy function will not compile. You can then put said dummy inside #ifndef NDEBUG guards to exclude it from release builds.

Billy ONeal
This is more like it. This gives me the compile-time assurance I was looking for.
mskfisher
... although for my purposes, I think you mean `const char *` pointers.
mskfisher
Yes -- Doh! Bill!
Billy ONeal