views:

119

answers:

6

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.

A: 

In visual-studio-2010 you can write:

const auto N = std::max_element( cont.begin(), cont.end() ) - cont.begin();
Kirill V. Lyadvinsky
What type of `mymember`? `auto`? I can't save N with `auto`.
Alexey Malistov
@Alexey Malistov: type of mymember is decltype(mymember).
Abyx
A: 

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).

Bart van Ingen Schenau
*"cast (which is guaranteed safe"* — That's how "The Year 2000 problem" was created.
Kirill V. Lyadvinsky
@Kirill: No, using a datatype (or print format) that was too small to hold all the values possible in the problem domain was the source of the Y2000 problem.
Bart van Ingen Schenau
I meant that you couldn't be sure how many elements can hold container. You can estimate limit of the current available platform, but the limit will grow. So the only guarantee is to use `difference_type` of a container.
Kirill V. Lyadvinsky
With regard to the Y2000 problem - in 50s everyone thought that 2 positions is enough to represent the year given the limited amount of memory. That was a good assumption for that time.
Kirill V. Lyadvinsky
@Kirill: The maximum capacity of a container may indeed change dramatically over time. But I am arguing that the actual number of items that will be stored does not depend on that capacity. It depends on considerations in the application domain. For example, when writing an inventory system for a warehouse, should I consider the possibility that there might be a warehouse with more than 4 billion distinct types of goods stored? Or would it be safe to say that the number of items in my container will never exceed that limit **for this application**?
Bart van Ingen Schenau
+1  A: 

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.

Abyx
There's no undefined behavior here, the C++ Standard says this is implementation-defined (4.7/3).
Kirill V. Lyadvinsky
A: 

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.

Steve M
That expression won't evaluate to a negative value even when `cont` *is* empty. When it's empty, `max_element` will return `end` (as of C++03), but `begin` will also equal `end`, so the difference will be zero.
Rob Kennedy
True, but he'll still have to test for emptiness at some point (or split it up into two statements and test for `max_element == end`), because 0 won't be a valid index in that situation.
Steve M
A: 

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.

Adrian McCarthy
+1  A: 

"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.,

Alf P. Steinbach