6

Here is my code:

#include <iostream>
#include <vector>

void cumulative_sum_with_decay(std::vector<double>& v)
{
    for (auto i = 2; i < v.size(); i++) {
        v[i] = 0.167 * v[i - 2] + 0.333 * v[i - 1] + 0.5 * v[i];
    }
}

void printv(std::vector<double>& v)
{
    std::cout << "{";
    for (auto i = 0; i < v.size() - 1; i++) {
        std::cout << i << ", ";
    }
    std::cout << v[v.size() - 1] << "}\n";
}

int main()
{
    auto v = std::vector<double>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    cumulative_sum_with_decay(v);
    printv(v);
}

When I try to compile and run this program, I get these warnings:

$ clang++ -std=c++11 -Wextra foo.cpp && ./a.out
foo.cpp:6:24: warning: comparison of integers of different signs: 'int' and 'std::__1::vector<double,
      std::__1::allocator<double> >::size_type' (aka 'unsigned long') [-Wsign-compare]
    for (auto i = 2; i < v.size(); i++) {
                     ~ ^ ~~~~~~~~
foo.cpp:14:24: warning: comparison of integers of different signs: 'int' and 'unsigned long'
      [-Wsign-compare]
    for (auto i = 0; i < v.size() - 1; i++) {
                     ~ ^ ~~~~~~~~~~~~
2 warnings generated.
{0, 1, 2, 3, 4, 5, 6, 7, 8, 8.68781}

How can I initialize these loop counters declared with auto such that the code is safe and there are no warnings?

Note that although I have a small vector here, I am trying to learn how to write safe code with auto even when the vector is so large that the value in i can exceed the range of integers.

Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • Have you tried `2u`? – Ken Y-N May 24 '18 at 06:44
  • 1
    @KenY-N `2u` will get rid of the warning but how can I be sure that the code is safe? What if the range of `std::vector::size_type` exceeds the size of `unsigned int`? The condition `i < v.size() - 1` is never going to be `false` then. – Lone Learner May 24 '18 at 06:47
  • How do we even know that `size()` returns an `unsigned` of whatever size? @songyuanyao's cast is probably the only way to be safe, as I've seen libraries that return `signed` types for `size`-like parameters. – Ken Y-N May 24 '18 at 06:51
  • @KenY-N I don't understand your last point. The C++ standard guarantees that the `size()` of standard containers returns an `unsigned` type. See http://en.cppreference.com/w/cpp/container/vector for example where they mention that the return type of `size()` is `size_type` which in turn is documented to be *size_type Unsigned integer type (usually std::size_t)*. – Lone Learner May 24 '18 at 07:30
  • @LoneLearner "The condition … is never going to be `false` then." – this is only true if the *actual* size of the vector exceeds `std::numeric_limits::max()`. I think you meant that but didn't write it as such. – Arne Vogel May 24 '18 at 14:49

5 Answers5

9

You can use 'decltype(v.size())' to get the correctly type.

for (decltype(v.size()) i = 2; i < v.size(); i++) 
nameless1
  • 203
  • 1
  • 6
5

The type of auto-declared variable is deduced from the initializer. given 2 or 0 it'll be int.

You could specify the type with explicitly typed initializer. e.g.

for (auto i = static_cast<decltype(v.size())>(2); i < v.size(); i++) {
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
2

If you care about matching the type exactly, you can write a helper for this (live example):

// Concepts would help here.
template<typename Cont, typename T>
auto as_size_type(const Cont& cont, T init) {
    return static_cast<decltype(std::size(cont))>(init);   
}

Usage:

for (auto i = as_size_type(v, 2); i < v.size(); i++) {
    v[i] = 0.167 * v[i - 2] + 0.333 * v[i - 1] + 0.5 * v[i];
}

I make use of std::size to handle differences like arrays vs. classes that have a ::size_type, but it's possible to move that responsibility to this helper if std::size isn't available. Similarly, the automatic return type deduction can use decltype etc. instead in C++11.

chris
  • 60,560
  • 13
  • 143
  • 205
0

This is not an answer to your question, but it solves your problem with use of iterators.

void cumulative_sum_with_decay(std::vector<double>& v)
{
    assert(v.size()>2);
    for (auto it = v.begin()+2; it!=v.end(); it++) {
        *it =  0.167 * *(it-2) + 0.333 * *(it-1) + 0.5 * *it;
    }
}

void printv(std::vector<double>& v)
{
    assert(v.size()>0);
    std::cout << "{";
    for (auto it = v.begin(); it != v.end() - 1; it++) {
        std::cout << *it << ", ";
    }
    std::cout << v.back() << "}\n";
}

int main()
{
    auto v = std::vector<double>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    cumulative_sum_with_decay(v);
    printv(v);
}
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
-3
auto i=2 

meaning a is a int type & vector.size() is size_t thats why -Wsign-compare came. you can use

for (std::size_t i = 0, max = vec.size(); i != max; ++i)

or

for (auto it = vec.begin(), end = vec.end(); it != end; ++it)
Deb S
  • 509
  • 5
  • 16
  • I didn't downvote but it'll be because the question specifically wants to use `auto` and not `size_t`, and also wants to be able to index earlier into the vector while iterating which isn't possible with your iterator example. – acraig5075 May 24 '18 at 06:55
  • What if the size of `std::size_t` is smaller than the size of `std::vector::size_type`? Then the condition `i < v.size()` is never going to be `false`. Also, how do you apply the iterator approach to my code without making the code more complicated? – Lone Learner May 24 '18 at 06:56
  • @LoneLearner *What if the size of std::size_t is smaller than the size of std::vector::size_type* --> This is impossible. Read the first sentence [here](http://en.cppreference.com/w/cpp/types/size_t) – llllllllll May 24 '18 at 07:05
  • @liliscent I see nothing there that says it is impossible. It says, *"std::size_t is the unsigned integer type of the result of the sizeof operator as well as the sizeof... operator and the alignof operator"*. If you read ahead, you see this, *"When indexing C++ containers, such as std::string, std::vector, etc, the appropriate type is the member typedef size_type provided by such containers."* If you see [std::vector](http://en.cppreference.com/w/cpp/container/vector) it says this, *"size_type Unsigned integer type (usually std::size_t)"*. What is *usually* true may not *always* be true. – Lone Learner May 24 '18 at 07:20
  • @LoneLearner *std::size_t can store the maximum size of a theoretically possible object of any type (including array).* – llllllllll May 24 '18 at 07:22
  • @liliscent Agreed, so my concern about `std::size_t` being larger than `std::vector::size_type` was unfounded. Now that you have proven that `std::size_t` is the theoretical maximum, I now have the opposite concern that `std::size_t` may be larger than what is necessary to represent `std::vector::size_type`. The point of using `auto` in my question was that the compiler would deduce the right type that is neither larger nor smaller but is the exact type necessary to represent `v.size()`. – Lone Learner May 24 '18 at 07:39