2014-09-26 4 views
10

Was ist der richtige Weg, um mit (sonst) konstanten Funktionen umzugehen, die einen zufälligen Generatoraufruf von C++ 11 zufälliger Klasse beinhalten? Sollten Sie es vorziehen, das konstante Flag der Funktion aufzugeben, oder wäre es besser, Generator und Distribution als veränderliche Elemente Ihrer Klasse zu deklarieren? Ein minimales Beispiel (nicht kompilieren) könnte sein:Konstante Korrektheit und <random>

#include <random> 

class foo 
{ 
    std::mt19937 MyGenerator; 
    std::normal_distribution<precision_type> gauss; 
    double get_rnd() const {return gauss(MyGenerator);} 
}; 
+0

Können Sie ein kleines Beispiel posten? Wenn die "random" -Instanz nicht versucht, eines der Mitglieder der Klasse zu ändern, und selbst kein Mitglied ist, kann die Funktion weiterhin "const" bleiben. – CoryKramer

+4

@ Cyber: Basierend auf der Frage scheint es klar, dass die "zufällige" Instanz ein Mitglied ist (Frage sagte "Element"). –

+3

Eine Überlegung kann Thread-Sicherheit sein. Die Const-Methode wird normalerweise als Thread-sicher behandelt. Wenn also der Aufruf durch Mutex geschützt ist, sieht es für mich aus, dass es ein veränderliches Mitglied ist, sonst nicht, wenn es eine geringe Chance auf Multithreading gibt. – dewaffled

Antwort

10

Es hängt wirklich davon ab, welche Semantik Sie const Mitglied Zugriff geben.

Für Standardklassen ist es Thread-sicher, gleichzeitig const Mitglieder aus mehreren Threads aufzurufen. Wenn man Mitglieder const belässt, die einen RNG mutieren, würde das brechen, es sei denn, der RNG ist vollständig Thread-sicher (für nicht const verwenden).

Sie müssen Ihre Klassen nicht auf die gleiche Weise entwerfen, aber andere Entwickler werden es wahrscheinlich verwirrend finden, dass Klassen, die nicht sicher "gelesen" werden können (const Elementfunktionen aufrufen) gleichzeitig erkannt werden.

Eine Option wäre, zwei Varianten zur Verfügung zu stellen - eine nicht konstante Version, die den intern gespeicherten RNG verwendet, und eine const Version, die einen RNG durch nichtkontinuierliche Referenz akzeptiert. (Die erste kann die zweite anrufen, keine const_cast erforderlich). Dadurch wird die Richtlinie "way.rot.thread-safety" implementiert, da mehrere Threads das Objekt sicher verwenden können, wenn jedes eine Thread-lokale RNG-Instanz bereitstellt. Es ermöglicht auch Tests mit einer falschen RNG-Implementierung, die wahrscheinlich noch wertvoller ist.

+1

Solange Sie den bereitgestellten RNG selbst erzeugen können (z. B. können Sie den Klassenkonstruktor bereitstellen), ist das Testen auch ohne einen Mock weniger problematisch, da Sie bereits deterministisch sind. Der Mock wird wertvoller, wenn Sie wünschen, dass der 6. Aufruf einen bestimmten Wert zurückgibt, der nicht in den ersten 5 zurückgegeben wird ... –

2

Das mutable Schlüsselwort wurde für diese Art von Fällen entworfen. Wenn Sie das Zufallsgenerator-Instanzfeld mit diesem Modifikator markieren, kann es seinen Status sogar in const Methoden der einschließenden Klasse ändern.

Im Allgemeinen scheint dies eine Grauzone zu sein, je nachdem, welche Art von Konzept Ihre Klasse darstellt. Wenn der Zustand des Generators für den Zustand dieser Klasse konzeptionell irrelevant ist, ist diese Lösung in Ordnung. Andernfalls sollten Sie das Design überdenken - wenn der Zustand des Generators relevant ist, sollte die Methode, die es verwendet, nicht const sein.

+1

Ich würde sagen, dass es in diesem Fall keine gute Idee ist. 'mutable' soll mit logisch-'const' Methoden helfen. Das Aufrufen eines RNG ist nicht logisch "const". –

+0

@OliverCharlesworth Das ist ein guter Punkt, aber ich würde argumentieren, dass es eine Grauzone ist und davon abhängt, welches Konzept die Klasse darstellt (aktualisiert die Antwort). – BartoszKP

+0

Ich nehme an, das gleiche Argument gilt für eine Methode, die die aktuelle Zeit oder die aktuelle Mausposition zurückgibt. – gnasher729

1

Ich denke, das hängt von Ihrem Anwendungsfall ab. Wenn Sie ein vollständig deterministisches Verhalten wünschen, müssen Sie das Flag const ablegen, um sicherzustellen, dass sich der Status Ihrer Klasse nicht ändern kann. Es ist nicht zu erwarten, dass dies geändert wird. Dies kann wichtig sein, wenn sicherheitsrelevanter Code oder Code geschrieben wird, der reproduzierbar getestet werden muss.

6

Es hängt wirklich davon ab, was Sie erreichen wollen, typische Strategien umfassen:

  1. Machen Wandelbarkeit. Markieren Sie die Methode einfach nicht als const.

  2. Externalisieren, um die Mutabilität zu exponieren. Übergeben Sie die veränderbaren Elemente (hier der Generator und die Verteilung) als Methodenparameter, wodurch die Verwendung von Mutabilität im Inneren offengelegt wird, ist der Aufrufer für jegliche Thread-Sicherheits-Implikation verantwortlich.

  3. Implementieren von "logischen" Konstanz.Wenn die Verwendung von Zufälligkeit nicht als Verletzung der logischen Konstanz der Klasse betrachtet wird, können Sie den Generator und die Verteilung einfach als mutable deklarieren. passen sie von faden Auswirkungen auf die Sicherheit (falls erforderlich, dh in einer Anwendung mit mehreren Threads, eine mutable Mutex verwenden)

Welche Alternative, die Sie auf die Semantik wählen, hängt Sie, welche zu erreichen.

+0

In C++ sagen wir * Memberfunktionen *;) –

+7

@BenVoigt: der Standard geht sicher, aber umgangssprachlich Anwendungen scheinen "Methoden" als Short-Hand zu bevorzugen :) –

+3

Der Begriff * Methode * hat Bedeutung in der Informatik. Die Benutzer, die sie wahllos in C++ anwenden, kommen entweder aus Java, haben Methoden und haben nicht ganz herausgefunden, dass C++ - Memberfunktionen nicht "virtuell" sind, oder Cargo- Kult-Programmierer wiederholen nur, was sie hören . –