Heeft het sleutelwoord ‘mutable’ een ander doel dan de variabele te laten wijzigen door een const-functie?

Een tijdje geleden kwam ik een code tegen die een membervariabele van een klasse markeerde met het mutabletrefwoord. Voor zover ik kan zien, kun je eenvoudig een variabele wijzigen in een const-methode:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

Is dit het enige gebruik van dit zoekwoord of is er meer aan de hand dan op het eerste gezicht lijkt? Sindsdien heb ik deze techniek in een klas gebruikt, waarbij ik een boost::mutexals veranderlijk markeerde, waardoor const-functies deze konden vergrendelen om veiligheidsredenen, maar om eerlijk te zijn, het voelt als een beetje hacken.


Antwoord 1, autoriteit 100%

Het maakt de differentiatie mogelijk tussen bitsgewijze const en logische const. Logische const is wanneer een object niet verandert op een manier die zichtbaar is via de openbare interface, zoals uw vergrendelingsvoorbeeld. Een ander voorbeeld is een klasse die een waarde berekent wanneer deze voor het eerst wordt aangevraagd, en het resultaat in de cache opslaat.

Sinds c++11 mutablekan worden gebruikt op een lambda om aan te geven dat dingen die door waarde zijn vastgelegd, kunnen worden gewijzigd (ze zijn niet standaard):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda

Antwoord 2, autoriteit 40%

Het mutabletrefwoord is een manier om de constsluier te doorbreken die u over uw objecten drapeert. Als je een const-referentie of verwijzing naar een object hebt, kun je dat object op geen enkele manier wijzigen behalvewanneer en hoe het is gemarkeerd als mutable.

Met uw constreferentie of pointer bent u beperkt tot:

  • alleen leestoegang voor zichtbare gegevensleden
  • toestemming om alleen methoden aan te roepen die zijn gemarkeerd als const.

De uitzondering mutablezorgt ervoor dat u nu dataleden kunt schrijven of instellen die zijn gemarkeerd als mutable. Dat is het enige uiterlijk zichtbare verschil.

Intern kunnen die const-methoden die voor u zichtbaar zijn, ook schrijven naar gegevensleden die zijn gemarkeerd als mutable. In wezen wordt de const sluier volledig doorboord. Het is volledig aan de API-ontwerper om ervoor te zorgen dat mutablehet concept constniet vernietigt en alleen in nuttige speciale gevallen wordt gebruikt. Het sleutelwoord mutablehelpt omdat het gegevensleden duidelijk markeert die onderhevig zijn aan deze speciale gevallen.

In de praktijk kun je constobsessief gebruiken in je hele codebase (je wilt in wezen je codebase “infecteren” met de const“ziekte”). In deze wereld zijn pointers en referenties constmet een paar uitzonderingen, wat code oplevert die gemakkelijker te beredeneren en te begrijpen is. Voor een interessante uitweiding kijk op “referentiële transparantie”.

Zonder het mutablesleutelwoord zul je uiteindelijk gedwongen worden om const_castte gebruiken om de verschillende nuttige speciale gevallen af te handelen (caching, ref counting, debug data, etc.) . Helaas is const_castaanzienlijk destructiever dan mutableomdat het de API clientdwingt om de const-bescherming van de objecten te vernietigen (zij) hij gebruikt. Bovendien veroorzaakt het wijdverbreide vernietiging van const: const_casthet gebruik van een const-pointer of -referentie maakt onbelemmerde schrijf- en methodeaanroeptoegang mogelijk voor zichtbare leden. Daarentegen vereist mutabledat de API-ontwerper fijnmazige controle uitoefent over de const-uitzonderingen, en meestal zijn deze uitzonderingen verborgen in const-methoden die werken op privé gegevens.

(NB ik verwijs een paar keer naar gegevens en methode zichtbaarheid. Ik heb het over leden die zijn gemarkeerd als openbaar versus privé of beschermd, wat een totaal ander type objectbescherming is dat wordt besproken hier.)


Antwoord 3, autoriteit 20%

Uw gebruik met boost::mutex is precies waar dit zoekwoord voor bedoeld is. Een ander gebruik is voor interne caching van resultaten om de toegang te versnellen.

In principe is ‘muteerbaar’ van toepassing op elk klassekenmerk dat geen invloed heeft op de extern zichtbare staat van het object.

In de voorbeeldcode in uw vraag is mutable mogelijk ongepast als de waarde van done_ de externe status beïnvloedt, dit hangt af van wat er in de …; onderdeel.


Antwoord 4, autoriteit 9%

Veranderbaar is voor het markeren van een specifiek kenmerk als aanpasbaar vanuit de const-methoden. Dat is zijn enige doel. Denk goed na voordat je het gebruikt, want je code zal waarschijnlijk schoner en leesbaarder zijn als je het ontwerp verandert in plaats van mutablete gebruiken.

http://www.highprogrammer.com/alan/rants/mutable.html

Dus als bovenstaande waanzin niet is wat?
veranderlijk is voor, waar is het voor? Hier is
het subtiele geval: veranderlijk is voor de
geval waarin een object logisch is
constant, maar in de praktijk moet
verandering. Deze gevallen zijn er maar weinig
tussen, maar ze bestaan.

Voorbeelden die de auteur geeft zijn onder meer caching en tijdelijke foutopsporingsvariabelen.


Antwoord 5, autoriteit 9%

Het is handig in situaties waarin je een verborgen interne status hebt, zoals een cache. Bijvoorbeeld:

class HashTable
{
...
openbaar:
  string lookup(string key) const
  {
    if(key == laatsteKey)
      retourneer laatsteWaarde;
    tekenreekswaarde = lookupInternal(sleutel);
    lastKey = sleutel;
    lastValue = waarde;
    winstwaarde;
  }
privaat:
  veranderlijke string lastKey, lastValue;
};

En dan kun je een const HashTable-object nog steeds zijn lookup()-methode laten gebruiken, die de interne cache aanpast.


Antwoord 6, autoriteit 3%

mutablebestaat zoals je afleidt om iemand toe te staan gegevens te wijzigen in een verder constante functie.

De bedoeling is dat je misschien een functie hebt die “niets doet” met de interne toestand van het object, en dus markeer je de functie const, maar het kan zijn dat je echt een aantal van de objecten staan op een manier die de juiste functionaliteit niet aantast.

Het sleutelwoord kan fungeren als een hint voor de compiler — een theoretische compiler zou een constant object (zoals een globaal) in het geheugen kunnen plaatsen dat gemarkeerd was als alleen-lezen. De aanwezigheid van mutablegeeft aan dat dit niet moet worden gedaan.

Hier zijn enkele geldige redenen om veranderlijke gegevens te declareren en te gebruiken:

  • Draadveiligheid. Het declareren van een mutable boost::mutexis volkomen redelijk.
  • Statistieken. Het aantal aanroepen van een functie tellen, gegeven sommige of alle argumenten.
  • Memo’s. Een duur antwoord berekenen en het dan opslaan voor toekomstig gebruik in plaats van het opnieuw te berekenen.

Antwoord 7, autoriteit 2%

Nou ja, dat is wat het doet. Ik gebruik het voor leden die zijn aangepast met methoden die de status van een klasse niet logischveranderen – bijvoorbeeld om het opzoeken te versnellen door een cache te implementeren:

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);
   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);
   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;
   // ...
private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

Je moet dit nu voorzichtig gebruiken – gelijktijdigheidsproblemen zijn een grote zorg, omdat een beller zou kunnen aannemen dat ze threadveilig zijn als ze alleen de const-methoden gebruiken. En natuurlijk zou het wijzigen van mutablegegevens het gedrag van het object niet op een significante manier moeten veranderen, iets dat zou kunnen worden geschonden door het voorbeeld dat ik gaf als, bijvoorbeeld, verwacht werd dat wijzigingen die naar schijf zou onmiddellijk zichtbaar zijn voor de app.


Antwoord 8, autoriteit 2%

Mutable wordt gebruikt wanneer je een variabele binnen de klasse hebt die alleen binnen die klasse wordt gebruikt om zaken aan te geven zoals bijvoorbeeld een mutex of een slot. Deze variabele verandert het gedrag van de klasse niet, maar is nodig om de threadveiligheid van de klasse zelf te implementeren. Dus als u zonder “mutable” geen “const”-functies kunt hebben, omdat deze variabele in alle functies die voor de buitenwereld beschikbaar zijn, moet worden gewijzigd. Daarom werd mutable geïntroduceerd om een lidvariabele schrijfbaar te maken, zelfs door een const-functie.

De gespecificeerde veranderlijke informeert zowel de compiler als de lezer dat het
veilig is en verwacht wordt dat een lidvariabele kan worden gewijzigd binnen een const
ledenfunctie.


Antwoord 9

mutable wordt voornamelijk gebruikt voor een implementatiedetail van de klasse. De gebruiker van de klasse hoeft er niets van te weten, daarom denkt hij dat “zou moeten” const kan zijn. Uw voorbeeld dat een mutex veranderlijk is, is een goed canoniek voorbeeld.


Antwoord 10

Uw gebruik ervan is geen hack, hoewel, zoals veel dingen in C++, veranderlijk kaneen hack zijn voor een luie programmeur die niet helemaal terug wil gaan om iets te markeren dat mag niet const zijn als non-const.


Antwoord 11

Gebruik “veranderbaar” wanneer voor dingen die LOGISCH stateloos zijn voor de gebruiker (en dus “const” getters zouden moeten hebben in de publieke klasse’ API’s) maar NIET stateloos zijn in de onderliggende IMPLEMENTATIE (de code in uw .cpp).

De gevallen die ik het vaakst gebruik, zijn luie initialisatie van ‘gewone oude gegevens’-leden zonder staat. Het is namelijk ideaal in de beperkte gevallen waarin dergelijke leden duur zijn om te bouwen (processor) of mee te nemen (geheugen) en veel gebruikers van het object er nooit om zullen vragen. In die situatie wil je een luie constructie aan de achterkant voor prestaties, aangezien 90% van de gebouwde objecten ze helemaal nooit hoeft te bouwen, maar je moet nog steeds de juiste staatloze API presenteren voor openbare consumptie.


Antwoord 12

Veranderbaar verandert de betekenis van constvan bitsgewijze const in logische const voor de klasse.

Dit betekent dat klassen met veranderbare leden langer bitsgewijze const zijn en niet langer verschijnen in alleen-lezen gedeelten van het uitvoerbare bestand.

Bovendien wijzigt het typecontrole door const-lidfuncties toe te staan om veranderbare leden te wijzigen zonder const_castte gebruiken.

class Logical {
    mutable int var;
public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};
class Bitwise {
    int var;
public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};
const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.
int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

Zie de andere antwoorden voor meer details, maar ik wilde benadrukken dat het niet alleen voor typeveiligheid is en dat het het gecompileerde resultaat beïnvloedt.


Antwoord 13

In sommige gevallen (zoals slecht ontworpen iterators) moet de klasse een telling of een andere incidentele waarde bijhouden, die niet echt van invloed is op de belangrijkste “status” van de klasse. Dit is meestal waar ik veranderlijk gebruik zie. Zonder mutable zou je gedwongen zijn om de hele consistentie van je ontwerp op te offeren.

Het voelt voor mij ook meestal als een hack. Handig in een zeer beperkt aantal situaties.


Antwoord 14

Het klassieke voorbeeld (zoals vermeld in andere antwoorden) en de enige situatie die ik tot nu toe heb gezien met het mutabletrefwoord dat tot nu toe is gebruikt, is het cachen van het resultaat van een ingewikkelde Getmethode, waarbij de cache is geïmplementeerd als een gegevenslid van de klasse en niet als een statische variabele in de methode (om redenen van delen tussen verschillende functies of pure netheid).

Over het algemeen zijn de alternatieven voor het gebruik van het trefwoord mutablemeestal een statische variabele in de methode of de truc const_cast.

Een andere gedetailleerde uitleg staat hier.


Antwoord 15

De mutable kan handig zijn wanneer u een const virtuele functie overschrijft en uw onderliggende klasselidvariabele in die functie wilt wijzigen. In de meeste gevallen zou je de interface van de basisklasse niet willen veranderen, dus je moet je eigen veranderlijke lidvariabele gebruiken.


Antwoord 16

Het veranderlijke sleutelwoord is erg handig bij het maken van stubs voor klassentestdoeleinden. Je kunt een const-functie afstompen en toch (veranderlijke) tellers of welke testfunctionaliteit dan ook die je aan je stub hebt toegevoegd, verhogen. Dit houdt de interface van de stubbed-klasse intact.


Antwoord 17

Een van de beste voorbeelden waarbij we veranderlijk gebruiken, is in deep copy. in copy constructor sturen we const &objals argument. Het nieuwe object dat wordt gemaakt, is dus van een constant type. Als we de leden in dit nieuw gecreëerde const-object willen wijzigen (meestal zullen we niet veranderen, in zeldzame gevallen kunnen we veranderen) moeten we het als mutabledeclareren.

mutableopslagklasse kan alleen worden gebruikt op niet-statische non-cont data-leden van een klasse. Een veranderlijk gegevenslid van een klasse kan worden gewijzigd, zelfs als het deel uitmaakt van een object dat als const is gedeclareerd.

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};
int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   
    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}
Output:-
X:123, Y:1

In het bovenstaande voorbeeld kunnen we de waarde van lidvariabele xwijzigen hoewel het deel uitmaakt van een object dat als const is gedeclareerd. Dit komt omdat de variabele xals veranderlijk is gedeclareerd. Maar als u de waarde van de lidvariabele yprobeert te wijzigen, geeft de compiler een fout.


Antwoord 18

Het sleutelwoord ‘veranderlijk’ is eigenlijk een gereserveerd sleutelwoord.vaak wordt het gebruikt om de waarde van een constante variabele te variëren.Als u meerdere waarden van een constante wilt hebben, gebruik dan het sleutelwoord veranderlijk.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   

Other episodes