-Wsign-conversion error when getting index for vector

Viewed 166
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <time.h>
int main()
{
    srand(time(NULL));
    std::vector<std::string> possible_result{"Rock", "Paper", "Scissors"};
    std::string selection{};

    std::cout << "(R)ock, (P)aper, (S)cissors: ";
    while(std::cin >> selection){
        if (selection != "R" && selection != "P" && selection != "S"){
            std::cout << "I didn't get that, try again.\n";
        } else {
            std::string election = possible_result[rand() % 3];
            std::cout << election << '\n';

        }
        std::cout << "(R)ock, (P)aper, (S)cissors: ";
    }

    return 0;
}

I'm trying to do a simple Rock, Paper, Scissors game, however when trying to compile I get:

"error: conversion to ‘std::vectorstd::__cxx11::basic_string<char >::size_type’ {aka ‘long unsigned int’} from ‘int’ may change the sign of the result [-Werror=sign-conversion]"

I'm using codeblocks, I have -Wsign-conversion in other compiler options, the program runs fine when I delete that line. I don't understand what the problem is, why does it compile without that line? How could I avoid getting that error?

1 Answers

As mentioned in the comments, the operand of the [] operation for the std::vector container is a size_t type, which is always an unsigned type (but it's bit width may vary between platforms). Thus, your compiler generates a warning (which you have instructed it to convert to an error) when you give a signed integer as its operand.

As also mentioned in that comment, you can silence the warning/error using an explicit cast on the [] operand, like so:

        std::string election = possible_result[static_cast<size_t>(rand() % 3)];

Note that you will likely receive a similar warning for your call to srand() – the time_t value returned by the time() call is most often a signed type (though this isn't explicitly mentioned by the Standard, IIRC) but srand expects an unsigned argument.

Further, as you're using C++, you should consider replacing your use of rand() with the far more versatile functions provided by the STL, in the <random> header. With these, you can dispense with the need for the % 3 operation (by specifying a range of 0 thru 2 to the generator) and also avoid the need for a cast (by specifying an unsigned type for that generator).

Here's a working code example:

#include <iostream>
#include <vector>
#include <random>
#include <ctime>  // Should really use "ctime" in place of "time.h" when using C++

int main()
{
    std::mt19937 gen(static_cast<unsigned int>(std::time(nullptr))); // Cast "time_t" to unsiged
    std::uniform_int_distribution<unsigned int> randoms(0, 2);// Set type & range (see below)
    std::vector<std::string> possible_result{ "Rock", "Paper", "Scissors" };
    std::string selection{};

    std::cout << "(R)ock, (P)aper, (S)cissors: ";
    while (std::cin >> selection) {
        if (selection != "R" && selection != "P" && selection != "S") {
            std::cout << "I didn't get that, try again.\n";
        }
        else {
            // In the call below, the random number is already "unsigned int"
            // and in the correct range...
            std::string election = possible_result[randoms(gen)];
            std::cout << election << '\n';
        }
        std::cout << "(R)ock, (P)aper, (S)cissors: ";
    }
    return 0;
}

Note that, even on systems where the size_t type is equivalent to unsigned long long int, the conversion of the randoms(gen) (unsigned int) operand of [] will be a 'safe' promotion, and will not (or should not) generate a warning.

Related