Search code examples
c++randomcompiler-errorsg++uniform-distribution

Initializing std::random_uniform_real_distribution with paranthesis


In the documentation of std::uniform_real_distribution, the notation for initializing the uniform real random number generator is to use round brackets:

std::uniform_real_distribution rand01(0,1)

However, when I define this as a member of a class variable:

#include <iostream>
#include <random>

class randomnumber
{
   public: 
      randomnumber()
      {
         std::random_device rd;
         generator.seed(rd()); 
      };

      double get_rn()
      {
         return rand01(generator);
      }

   private :
      std::mt19937 generator; 
      std::uniform_real_distribution<double> rand01(0.,1.);
};

int main(int argc,char *argv[])
{
   randomnumber randnum;
   for(int i=0;i<100;i++)
      std::cout << randnum.get_rn() << std::endl;
}

The compiler fails with an error:

randtest.cpp:20:53: error: expected identifier before numeric constant
   20 |       std::uniform_real_distribution<double> rand01(0.,1.);
      |                                                     ^~
randtest.cpp:20:53: error: expected ‘,’ or ‘...’ before numeric constant
randtest.cpp: In member function ‘double randomnumber::get_rn()’:
randtest.cpp:15:24: error: cannot convert ‘std::mt19937’ {aka ‘std::mersenne_twister_engine<long unsigned int, 32, 624, 397, 31, 2567483615, 11, 4294967295, 7, 2636928640, 15, 4022730752, 18, 1812433253>’} to ‘int’
   15 |          return rand01(generator);
      |                        ^~~~~~~~~
      |                        |
      |                        std::mt19937 {aka std::mersenne_twister_engine<long unsigned int, 32, 624, 397, 31, 2567483615, 11, 4294967295, 7, 2636928640, 15, 4022730752, 18, 1812433253>}
randtest.cpp:20:53: note:   initializing argument 1 of ‘std::uniform_real_distribution<double> randomnumber::rand01(int)’
   20 |       std::uniform_real_distribution<double> rand01(0.,1.);

The fix is to use curly brackets {} instead of the round brackets ().

The question is, is this fix a fundamental fix? Meaning, can I trust the fixed code with {} to always generate uniform random number between 0 and 1?
Also for the future purposes for similar cases, it would be greaet if someone can explain me why compiler behaves in this way.

I use for compiling g++ g++ -std=c++11 randtest.cpp:

g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Solution

  • is this fix a fundamental fix?

    For this code, yes. But not in general.

    can I trust the fixed code with {} to always generate uniform random number between 0 and 1?

    Yes, std::uniform_real_distribution<double> rand01{0.,1.}; defines a uniform distribution with parameters of 0 and 1, by calling the exact same constructor that std::uniform_real_distribution rand01(0,1) does for non-class scopes.

    But the way that the compiler searches for constructors is different when braces are used. Particularly for classes intended to work as collections (e.g. std::vector), braces may choose a different constructor. A strong sign of this happening is when the class has a constructor (or multiple) for some std::initializer_list<...>.

    std::uniform_real_distribution::uniform_real_distribution() doesn't have special handling for initializer lists which is what makes your specific example safe.