What type should I use for iterator difference to

2019-05-07 23:34发布

问题:

I need a common rule for warnings in x64 mode. Which way is better?

Consider the following lines of some code

const int N = std::max_element(cont.begin(), cont.end()) - cont.begin();

or

const int ARR_SIZE = 1024;
char arr[ARR_SIZE];
//...
const int N = std::max_element(arr, arr + ARR_SIZE) - arr;

It is my usual code. I have no problems with x86.

But if I run compiler in x64 mode I have some warnings:

conversion from 'std::_Array_iterator<_Ty,_Size>::difference_type' to 'int', possible loss of data
conversion from '__int64' to 'int', possible loss of data

I want to solve these problems by common rule. Which way is better?

  1. Making static_cast:

    const int N = static_cast<int>(
         std::max_element(cont.begin(), cont.end()) - cont.begin()  );
    

    I think this is not general-purpose. And too much letters.

  2. Replace output type with ptrdiff_t:

    const ptrdiff_t N = std::max_element(cont.begin(), cont.end()) - cont.begin();
    

    What should I do then with this unknown type ptrdiff_t? I'll get another dozen warnings then. I want to make many operations with N: save, addition, multiplication, cycles and etc. Important: but what if std::_Array_iterator<_Ty,_Size>::difference_type and ptrdiff_t are different types?

  3. Replace output type with std::_Array_iterator<_Ty,_Size>::difference_type:

    //h-file
    struct S {
        type mymember; // What is the type?
    };
    
    
    //cpp-file
    typedef std::vector<int> cont_t;
    const cont_t::difference_type N = std::max_element(cont.begin(), cont.end()) - cont.begin();
    // Save N
    S mystruct;
    mystruct.mymember = N; // What type of mystruct.mymember?
    

    How should I save N? What type of mystruct.mymember? I don't know it in h-file.

  4. Your solution.

回答1:

"what if std::_Array_iterator<_Ty,_Size>::difference_type and ptrdiff_t are different types?" Don't use such a compiler. Also, chances are that it can't formally be different. E.g. this is the case for a vector using the default standard allocator, since that's where it fetches its typedefs, but since the formal guarantee doesn't matter (he he, it really doesn't) I'm not going to look this up in the C++0x draft.

So, use ptrdiff_t.

But it can be a good idea to add a few typedefs, like

typedef ptrdiff_t Size;
typedef ptrdiff_t Index;

and then in your concrete case you'd use Index.

These typedefs are naturally accompanied by custom freestanding countOf, startOf and endOf functions, enabling you to treat raw arrays and standard library containers in exactly the same way.

When you see the name Index it's a bit more clear that it's an index, which can't very naturally get out of the Index or Size set of types almost no matter what you do. E.g., add something to it, it's still an Index. So mostly there will not be a "another dozen warnings".

But in some rare case you'll need to get from Index to just int, say. In and in those rare cases just do a static_cast to shut up the compiler and make your intent clear. Or even a custom static_cast-like narrowTo operation, for expressiveness...

Cheers & hth.,



回答2:

To keep result of max_element() - cont.begin() you should use

struct Foo { std::vector<int>::difference_type n; };

or

template<typename T> struct Foo { std::vector<T>::difference_type n; };

or

template<T> struct Foo { T n; };

Because difference_type is difference_type, and when you cast it to int you get undefined behavior.

You can use &*c.begin() to convert iterator to pointer, and use ptrdiff_t for difference of this pointers.



回答3:

I'd use std::ptrdiff_t.

I can't think of a reasonable implementation where std::vector<T>::iterator::difference_type would not be assignable to a std::ptrdiff_t. They're almost certainly going to be the same. If they are not the same, the difference_type would have to be smaller than ptrdiff_t.

Also, ptrdiff_t is a signed type, so if all your code is designed to work with ints, you'll be better off than if you tried to use an unsigned type, like std::vector<int>::size_type.



回答4:

In visual-studio-2010 you can write:

const auto N = std::max_element( cont.begin(), cont.end() ) - cont.begin();


回答5:

My solution is to use a type that is known to be large enough, based on the domain knowledge that I have but which may not be available to the compiler. If the compiler then complains about a possible loss of data, I add a cast (which is guaranteed safe, because I know beforehand that the target type must be large enough).



回答6:

Use std::vector<int>::size_type:

  • It is guaranteed to represent any non-negative value of difference_type
  • It's what all of vector's indexing operations accept
  • If cont is non-empty, std::max_element(cont.begin(), cont.end()) - cont.begin(); will not evaluate to a negative value. If it is empty, then you shouldn't be doing any of this processing anyhow.

What should I do then with this 'unknown' type? I'll get another dozen warnings then. I want to make many operations with N: save, addition, multiplication, cycles and etc.

You won't get any warnings if you use the type consistently and limit the usage to where you actually need it. N is an index into a vector; that's all it's good for. Any meaningful operation you perform on it is going to result in another possible index into a vector.