10

What is the correct way to deal with (otherwise) constant functions that include a random generator call of C++11's random-class? Should you prefer giving up the constant flag of the function or would it be better to declare generator and distribution as mutable elements of your class? A minimal example (not compiling) might be:

#include <random>

class foo 
{
  std::mt19937 MyGenerator;
  std::normal_distribution<precision_type> gauss;
  double get_rnd() const {return gauss(MyGenerator);}
};
user3058865
  • 460
  • 1
  • 4
  • 10
  • Could you post a small example? If the `random` instance is not attempting to mutate any of the class's members, and is not itself a member, then the function can still remain `const`. – Cory Kramer Sep 26 '14 at 12:32
  • 4
    @Cyber: Based on the question it seems clear that the `random` instance IS a member (question said "element"). – Ben Voigt Sep 26 '14 at 12:34
  • 3
    One consideration may be thread safety. Const method usualy treated as thread-safe. so, if the call is protected by mutex inside, for me it looks like it's ok to make it mutable member, otherwise not if there is a slight chance of multithreading involved. – dewaffled Sep 26 '14 at 12:35
  • 1
    If it returns different values every time it seems a little odd to want to declare it constant. This will confuse the user. Users expect that if you call a method of a const object it will be idempotent. – Ben Sep 26 '14 at 12:37
  • @Cyber: A minimal example was added to the question. – user3058865 Sep 26 '14 at 12:38
  • Is there a possibility that the compiler may use the information to optimise the method call away as per this answer? http://stackoverflow.com/questions/212237/constants-and-compiler-optimization-in-c – Ben Sep 26 '14 at 12:42
  • @Ben: No, `const` member functions have a `const` view of the object, which in no way suggests that the object's dynamic type is `const`. To quote Michael Burr's accepted answer you linked: "it does not apply to objects that are accessed through const pointers or references because those access paths can lead to objects that are not const." – Ben Voigt Sep 26 '14 at 12:43
  • @BenVoigt, but it allows you to declare and use a `const` instance, no? `const foo myFoo; double r1= myFoo.get_rnd();` which will statically known to be const. Anything forbidding optimisation of `while(myFoo.get_rnd() > 0.5){}`? – Ben Sep 26 '14 at 12:49
  • @Ben: Well, `const` really has no effect on that. If the internals of the function are visible at the point of call, the compiler can optimize based on full information. No optimization is possible for a call to a black box, not based on Standard C++ function signatures. (Compiler extensions such as "pure" attributes can enable optimization, but `const` does not mean "pure".) – Ben Voigt Sep 26 '14 at 12:54

4 Answers4

11

It really depends on what semantics you give to const member access.

For Standard classes, it is thread-safe to concurrently invoke const members from multiple threads. Leaving members const that mutate an RNG would break that, unless the RNG is fully thread-safe (for non-const use).

You don't have to design your classes the same way, but other developers will probably find it confusing to discover classes that can't be safely "read" (invoke const member functions) concurrently.

One option would be to provide two variations -- a non-const version that uses the internally stored RNG, and a const version that accepts an RNG by non-const reference. (The first can call the second, no const_cast required). This implements the "pay only for what you need" guideline w.r.t thread-safety, as multiple threads can safely use the object if each provides a thread-local RNG instance. It also allows testing using a mock RNG implementation, which is likely even more valuable.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    As long as you can seed the provided RNG yourself (for example, the class constructor allows you to provide it), then testing is less of an issue even without a mock as you are already deterministic. The mock becomes more valuable when you wish for the 6th call to return a specific value not returned in the 5 first... – Matthieu M. Sep 26 '14 at 12:46
6

It really depends on what you want to achieve, typical strategies include:

  1. Exposing mutability. Simply do not mark the method as const.

  2. Externalising to expose mutability. Pass the mutable elements (here the generator and distribution) as method parameters, exposing the use of mutability inside, the caller is responsible for any thread-safety implication.

  3. Implementing "logical" constness. If the use of randomness is not considered as breaking the logical constness of the class, you can simply declare the generator and distribution as mutable; beware of thread-safety implications (if necessary, ie in a multi-threaded application, use a mutable mutex)

Which alternative you pick depends on the semantics you which to achieve.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • In C++ we say *Member functions* ;) – Ben Voigt Sep 26 '14 at 12:51
  • 7
    @BenVoigt: the Standard does, for sure, but colloquial uses seem to favor "methods" as a short-hand :) – Matthieu M. Sep 26 '14 at 12:53
  • 3
    The term *method* has meaning in computer science. The users applying it indiscriminately in C++ either come from Java, which *does* have methods, and haven't quite figured out that C++ member functions are not `virtual`-by-default, or cargo-cult programmers just repeating what they hear. – Ben Voigt Sep 26 '14 at 12:56
  • 2
    I prefer the term non-virtual parametrically invocated code segments, personally – Matthew Sep 26 '14 at 17:50
2

The mutable keyword was designed for this type of cases. When you will mark the random generator instance field with this modifier it will be allowed to change its state even in const methods of the enclosing class.

In general this seems to be a grey area depending on what type of concept your class is representing. If the state of the generator is conceptually irrelevant to the state of this class than this solution is OK. Otherwise you should rethink the design - if the state of generator is relevant, than the method using it should not be const.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • 1
    I'd say it's not a good idea in this case, though. `mutable` is to help out with logically-`const` methods. Invoking an RNG is not logically-`const`. – Oliver Charlesworth Sep 26 '14 at 12:37
  • @OliverCharlesworth That's a good point, however I'd argue that it's a grey area and depends on what concept the class is representing (updated the answer). – BartoszKP Sep 26 '14 at 12:38
  • I suppose the same argument applies to a method that returns the current time, or the current mouse position. – gnasher729 Sep 26 '14 at 15:17
1

I think that depends on your use case. if you want some totally deterministic behavior you have to drop the const flag to make sure the state of your class can not change then it is not expected to be changed. This may be important if writing safety relevant code or code that have to be reproducible tested.

vlad_tepesch
  • 6,681
  • 1
  • 38
  • 80