Boost logo

Boost :

From: Jesse Jones (jejones_at_[hidden])
Date: 2000-10-19 18:14:47


The formatting of this will be a bit screwed up (Adobe's email went down
yesterday and I had to pull John's message off egroups.com).

I said:
>The pointer case is similiar: the first sentence in section 3.9.3 can be
>read to mean that const pointer types are not pointer types. Is this
>interpretation correct?

John said:
I'm tempted to say that what was in the mind of the committee isn't that
relevant here (they probably didn't even think about this), the point is
that the semantics follow the letter of the standard as written, and much
more to the point are actually useful as written, let me try and present
some examples to make my case:

Me:
Well, my guess is that the committee didn't quite think through what they
were trying to say in 3.9.1-3.93. If that's true then it may not be worth
following the letter of the standard. I don't know which interpretation is
more useful, but the current behavior is definitely surprising so I think
it's the correct choice only if it is clear that it is more useful.

John's first example:
Example 1: Specialising vector for pointers.

[snip]

Now consider what happens if T is int* const: should we still be using the
void* specialisation for this?

I think the answer is no, in fact cv-qualified types are not even
containable at all, there is no way we can rely on being able to
instantiate vector<const int> or vector<int*const> (because a const-type is
not an assignable type), nor can we instantiate vector<int volatile>
(because the copy constructible guarantees break down).

Me:
My initial thought was that you'd get compile errors if you tried to do
anything useful with the vector, but that doesn't seem to be true. It looks
like you can probably do everything with a bit of care. So, this seems to
be an instance where the current behavior is better.

John's 2-4 examples:
<snip [const pointer's are not iterators, not always assignable, not always
copyable]>

Me:
There's no doubt that const pointers differ from unqualified pointers, but
it's arguable that problems should be handled by either a compile time
assert (hopefully with a custom error message) or by letting the compiler
barf on a line like ++iter.

John's last example:
Example 5: We can use these classes for static assertions:
In the rational class, we may require that the template argument is an int:

template <class T>
class rational
{
BOOST_STATIC_ASSERT(is_integer<T>::value);
/*details*/
};

However in this case "must be an int" means "must be a non-cv-qualified
int", so the current rules are correct in this case.

Me:
But if the rule was changed wouldn't the other assertions fire? This seems
better. If the user was silly enough to pass in a const int the is_integer
assert firing would likely confuse the hell out of him. At least with the
addable assert he'd have a chance at seeing the problem.

John:
I'm going to stick my neck out here and say that instantiating a template
on a cv-qualified type is almost always an error, there are clearly
exceptions (std::pair springs to mind), but I believe that they are
exceptions, in fact I've tried hard to think of a non-trivial example of
where is_pointer would be used without regard to cv-qualifiers and I've
failed.

Jesse, did you have some particular usage examples in mind to contradict
this - I'm still open to persuasion here.

Yours-with-neck-stuck-out-waiting-for-Madame-Guillotine's-kiss!

Me:
Brave man. :-) Here's an example of using is_pointer without regard to
cv-qualifiers. A std::copy implementation written using meta-programming
idioms:

#include <string>

// is_pointer (const pointers are considered to be pointers)
template <typename T> struct is_pointer { static const bool value = false; };
template <typename T> struct is_pointer<T*> { static const bool value =
true; };
template <typename T> struct is_pointer<const T*> { static const bool value
= true; };

// is_POD (simplified implementation)
template <typename T> struct is_POD { static const bool value = false; };
template <> struct is_POD<std::string> { static const bool value = false; };
template <typename T> struct is_POD<T*> { static const bool value = true; };
template <> struct is_POD<int> { static const bool value = true; };

// Copy Helpers
namespace details {
    template <typename InputIter, typename ForwardIter, bool optimize>
    struct Copy {
        static ForwardIter Do(InputIter first, InputIter last, ForwardIter
result)
        {
            for (; first < last; ++first, ++result)
                *result = *first;
            return result;
        }
    };

    template <typename InputIter, typename ForwardIter>
    struct Copy<InputIter, ForwardIter, true> {
        static ForwardIter Do(InputIter first, InputIter last, ForwardIter
result)
        {
            using namespace std;

            size_t n = static_cast<size_t>(last - first);
            memcpy(result, first, n*sizeof(*first));
            return result + n;
        }
    };
}

// Copy
template <typename InputIter, typename ForwardIter>
inline ForwardIter Copy(InputIter first, InputIter last, ForwardIter result)
{
    using namespace details;

    typedef std::iterator_traits<InputIter>::value_type T;

    enum {optimize = is_pointer<InputIter>::value*
                     is_pointer<ForwardIter>::value*
                     is_POD<T>::value};

    return Copy<InputIter, ForwardIter, optimize>::Do(first, last, result);
}

int main()
{
    int i[10];
    int j[10];
    Copy(i, i+10, j);

    std::string s[10];
    std::string t[10];
    Copy(s, s+10, t);

    return 0;
}

  -- Jesse


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk