Search code examples
c++c++11exceptionglobal-variables

How to deal with static storage duration warnings?


I'm a total newbie trying to learn C++ from a book. The code below works and produces output as expected, but there are warnings on the two lines that define engine and randomInt: "Initialization of 'engine' with static storage duration may throw an exception that cannot be caught."

If I put lines 7 and 8 inside of main(), the warnings completely go away, but then engine and randomInt are not available to getNumber.

I don't know how to fix the warnings. Also, perhaps more importantly, what is the proper approach for using randomInt in various places besides main()? Is it proper to declare it in main() then pass it to functions as needed? Somehow main() doesn't feel like the proper spot to be declaring these types of things.

I asked a question similar to this one earlier, but I'm still struggling to understand, and have provided an example that's hopefully useful.

// Loosely based on Fig. 6.12: fig06_12.cpp, C++ How To Program, Ninth Edition

#include <iostream>
#include <iomanip>
#include <random>

std::default_random_engine engine( static_cast<unsigned int>( time(nullptr) ) );
std::uniform_int_distribution<unsigned int> randomInt( 1, 6 );

int getNumber();

int main() {
    for ( unsigned int counter = 1; counter <= 10; ++counter ) {
        std::cout << std::setw( 10 ) << randomInt( engine );
        if ( counter % 5 == 0 )
            std::cout << std::endl;
    }
    std::cout << getNumber() << std::endl;
    return 0;
}

int getNumber () {
    return randomInt( engine );
}

Output:

/CLionProjects/Warning/cmake-build-debug/Warning
         3         5         6         3         3
         1         4         2         4         5
2

Process finished with exit code 0

Solution

  • Using global variables is problematic, and it is common wisdom to avoid them unless they're absolutely necessary. For details, see:

    Are global variables bad?

    Your question title also regards non-global-scope static storage duration variables (e.g. static locals of functions); these are less problematic but can also give you some headaches, especially in multi-threaded work.

    Bottom line: It's best to make your functions depend only on their parameters and have as few side-effects as is necessary. Let's do this with your getNumber() function:

    template <typename Distribution>
    typename Distribution::result_type getNumber (
        std::default_random_engine&  random_engine,
        Distribution&                distribution) 
    {
        return distribution( random_engine );
    }
    
    int main()
    {
        std::default_random_engine engine( static_cast<unsigned int>( time(nullptr) ) );
        std::uniform_int_distribution<unsigned int> randomInt( 1, 6 );
    
        for ( unsigned int counter = 1; counter <= 10; ++counter ) {
            std::cout << std::setw( 10 ) << randomInt( engine );
            if ( counter % 5 == 0 )
                std::cout << std::endl;
        }
        std::cout << getNumber( engine, randomInt ) << std::endl;
        return 0;
    }