Avatar billede stringbuffer Nybegynder
19. december 2001 - 12:09 Der er 15 kommentarer og
1 løsning

const T&operator [](...) const

Jeg har lavet en template Hashtable class, den har 2 versioner af [] operatoren:

T& operator [](const char*)
og
const T& operator [](const char*) const

Såvidt jeg har forstået så skal const operatoren kaldes (ANSII C++) hvis man laver noget i stil med:
T val = (const T&)Table[\"blah\"];
Men det sker ikke!!!!!!
Den kalder ALTID den, der ikke er const! - og det er ligemeget hvilken rækkefølge jeg erklærer dem i.

Hvad skal jeg gøre for at instruere compileren (g++) til at bruge const operatoren?
Avatar billede chries Nybegynder
19. december 2001 - 12:16 #1
jeg undrer mig at compileren ikke klager, basalt set er de ens. ifølge standarden kan overloadede funktioner ikke skælnes på retur typen, så den eneste forskel på de to funktioner er at du har højt og hellig lovet ikke at gøre noget i den ene (pille i data).
Avatar billede chries Nybegynder
19. december 2001 - 12:22 #2
Jeg kunne forstille mig den ville kalde const funktionen hvis du kaldte den fra en const funktion ?
Avatar billede stringbuffer Nybegynder
19. december 2001 - 12:23 #3
Selvfølgelig brokker compileren sig ikke, det er ANSII standard at gøre det sådan!
Avatar billede chries Nybegynder
19. december 2001 - 12:42 #4
Hvis det er standard, hvorfor kalder den så ikke ? :-)

Hvis det er C++, burde du så ikke anvende C++ cast operatorerne i stedet for C\'s, eller vil det ikke gøre en betydelig forskel ?
static_cast<const T&>(Table[\"blah\"]);

Måske er det bare en compiler fejl (bug) ?

Jeg ved ikke som visual c++ 5 er ansi c++ 100% men følgende ,

class test
{
    int& operator[]( char param );
    const int& operator[]( char param ) const;

    int a;
};

giver følgende fejl:
overloaded functions only differ by return type
Avatar billede stringbuffer Nybegynder
19. december 2001 - 13:11 #5
Jeg ved ikke med VC 5, men gcc skulle i hvert fald være ANSII.
Avatar billede soepro Nybegynder
19. december 2001 - 13:40 #6
stringbuffer >> Det du har lavet er som cries skriver overloading af en operator - og det kræver at der er forskel i parametrene til funktionen. Return-type bliver IKKE -gentager IKKE- betragtet i forhold til overloading. Derfor er det heller ikke muligt at lave dette her:

float fromInt(int value);
char* fromInt(int value);

hvilke enhver compiler bør brokke sig over. Jeg må jo spørge mig selv, hvorfor du overhovedet vil have en const variant - den er jo til fulde dækket at du \"non-const\" variant. Hvis det er for at beskytte char* variablen, skulle du kunne gøre dette:

T& operator [](const char*);
T& operator [](char*);

idet const char* og char* ikke er det samme. Når du kalder operatoren skal du selv \"vælge\" hvilken af de to du vil bruge vha. const modifieren:

T val = Table[const \"blah\"];

Du kan bl.a. i C++ Builder overstyre typen af char* (\'unsigned char\' eller noget i den stil) hvilken kunne betyde at din compiler oversætter dine function call inden compilering, og at de to funktioner derfor ender med at være ens.
Avatar billede soepro Nybegynder
19. december 2001 - 13:42 #7
dvs. de bliver til:

T& operator[](unsigned char*);
T& operator[](unsigned char*);

hvilke de fleste compilere accepterer - netop fordi de er ens.
Avatar billede stringbuffer Nybegynder
19. december 2001 - 19:28 #8
Det er ikke mit argument, jeg skal beskytte, men det objekt, operatoren kaldes på.

Hvis man kalder operatoren med et index, som ikke findes i tabellen i forvejen, bliver denne oprettet (af den operator, der ikke er const).
Det er nødvendigt for at lave en assignment, f.eks.
Tabel[\"noget\"] = 112;

Derimod er der kun brug for en const returtype når man assigner den anden vej:

int noget = Tabel[\"noget\"];

Det er såvidt jeg ved derfor, man laver denne overloading, og derfor brokker en fornuftig compiler sig ikke over det.
Avatar billede stringbuffer Nybegynder
19. december 2001 - 19:33 #9
Hvis man f.eks. vil tjekke en masse ord for om de findes i tabellen, vil der blive oprettet en ny celle i tabellen for hvert nyt ord. Det er da lidt tåbeligt når det er almindeligt kendt at en hashtabel mister meget af sin effektivitet når fyldningsfaktoren bliver over 80%.

Hvis man kan bruge en operator ... [] const vil tabellen ikke vokse. Det er bare underligt at compileren ikke bruger den.
Avatar billede soepro Nybegynder
20. december 2001 - 08:41 #10
stringbuffer >> ??? Det forstod jeg ikke en meter af.

Du kan da ikke checke forekomsten af et givent ord, ved at lave et ASSIGNMENT på den !?! I øvrigt er det da dig der styrer hvordan indexet \"noget\" bliver anvendt. Hvis du skal checke om et givent ord er i din tabel, skal du da lave dette check:

if (Tabel[\"et givent ord\"] != 0)
{ // Er i tabellen.
  Tabel[\"et givent ord\"] += 1;
}
else
{
  // Ordet er ikke i tabellen.
}

Idet jeg antager at din hash-tabel indeholder elementer der ser sådan her ud:

typedef struct {
  string ord;
  int    refCount;
} hashTblElm;
Avatar billede stringbuffer Nybegynder
20. december 2001 - 23:03 #11
soepro >> !!! jeg kan da ikek gøre for at du ikke forstår

Jeg har da ikke nogen sted skrevet at jeg vil tjekke forekomsten ved at lave en assignment, faktisk gør jeg det lige præcis som du skriver ovenfor.

Men lige så snart man laver noget i stil med
if (Tabel[\"ord\"] != 0) ...
bliver der oprettet en tabel element til dette ord. Selvom man bare vil læse en const værdi!
Avatar billede soepro Nybegynder
21. december 2001 - 09:17 #12
Hvilket bringer mig tilbage til min første kommentar om at der må være koden i din hash-table template der er forkert.

Kunne du ikke poste koden her, så vi kan kigge på den.
Avatar billede stringbuffer Nybegynder
21. december 2001 - 15:08 #13
Koden virker fint, det kan ikke lade sig gøre at lave en [] operator, der ikke opretter et nyt tabel element hvis denne skal kunne bruges sådan:
Tabel[\"Nyt element\"] = noget;
Men det er ikke nødvendigt at oprette et nyt element i tabellen når man gør det her:
noget = Tabel[\"ord\"];

Men OK, hvis du virkeligt SKAL overbevises, er koden her:

// hashtable.h
#ifndef CLASS_HASH_SPEC
#define CLASS_HASH_SPEC

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include \"defines.h\"

template <class T>
class Hashtable
{
public:
    // Default constructor
    Hashtable(const unsigned int=31);

    // Copy constructor
    Hashtable(const Hashtable<T>&);

    // Destructor
    ~Hashtable();

    // Show how the values are placed
    void list();

    // Subscript operator, for inserting
    const T& operator[](const char*) const;
   
    // Read-only subscript
    T& operator[](const char*);

    // Iterator functions, return the key value
    const char* Next();
    const char* Next(T&);

    const char* First();
    const char* First(T&);

    int Count() { return count; }

    // Remove all entries
    void Clear();

private:

    T T_Default;

    class Cell;
    typedef Cell* P_Cell;

    // Cell for holding the key and the data
    class Cell
    {
    public:
        Cell(const char* key) : value(), p_next(NULL), p_nextval(NULL)
    {
            if ((key_value = new char[strlen(key)+1]) == NULL)
        {
        perror(\"Cannot allocate hash-table key\");
        exit(1);
        }
        strcpy(key_value, key);
    }
    ~Cell() { delete [] key_value; }

    // The key
    char* key_value;

    // Data value
    T value;

    // Pointer to next cell in chain
    P_Cell p_next;

    // Pointer to next value (in chronological order)
    P_Cell p_nextval;
    };

    // The hash table, a vector of pointers to data cells
    P_Cell *the_p_cell_vec;
   
    // First cell inserted
    P_Cell first_cell;

    // Currently last cell
    P_Cell last_insert_cell;

    // Cell pointed to by the iterator
    P_Cell current_cell;

    // Size of the table vector
    int the_no_elements;

    int count;

    // Hash function
    int hash(const char*);
};

#include \"hashtable.cpp\"

#endif

// hashtable.cpp
#ifndef CLASS_HASH_IMP
#define CLASS_HASH_IMP


#include \"hashtable.h\"


template <class T>
Hashtable<T>::Hashtable(const unsigned int size):
    T_Default(), first_cell(NULL), last_insert_cell(NULL), current_cell(NULL)

{
    the_no_elements = size;
    count = 0;

    // Illegal size
    if ( the_no_elements < 1 )
    {
    printf(\"Hashtable has invalid bounds\\n\");
    exit(1);
    }
       
    // Allocate P_Cell vactor, that is the hash table itself
    if ((the_p_cell_vec = new P_Cell[the_no_elements]) == NULL)
    {
        perror(\"Cannot create hashtable\");
    exit(1);
    }
       
    // Initialize all elements to NULL
    for(int i=0; i<the_no_elements; i++)
    the_p_cell_vec[i] = NULL;
}


template <class T>
Hashtable<T>::Hashtable(const Hashtable<T>& ht):
    first_cell(NULL), last_insert_cell(NULL), current_cell(NULL)
{
    int i;
    char * c;
    T t;
    the_no_elements = ht.the_no_elements;
    count = ht.count;
   
    if ((the_p_cell_vec = new P_Cell[the_no_elements]) == NULL)
    {
        perror(\"Cannot create hashtable\");
    exit(0);
    }

    // Initialize all elements to NULL
    for(i=0; i<the_no_elements; i++)
    the_p_cell_vec[i] = NULL;

    // Copy using the iterator. Not very efficient though
    for (c = ht.First(t); c != NULL; c = ht.Next(t))
    (*this)[c] = t;
}

template <class T>
Hashtable<T>::~Hashtable()
{
    current_cell = first_cell;

    while (current_cell != NULL)
    {
    first_cell = current_cell;
    delete first_cell;
    current_cell = current_cell->p_nextval;
    }
   
    delete [] the_p_cell_vec;
}

template <class T>
void Hashtable<T>::Clear()
{
    P_Cell p_cell, p_current;
   
    for(int i=0; i<the_no_elements; i++)
    {
    p_cell = the_p_cell_vec[i];
   
    // Clear the chain
    while (p_cell != NULL)
    {
        p_current = p_cell;
        p_cell = p_cell->p_next;
        delete p_current;
    }
    the_p_cell_vec[i] = NULL;
    }
   
    // Reset internal iterator
    first_cell = NULL;
    last_insert_cell = NULL;
    current_cell = NULL;

    count = 0;
}

template <class T>
const T& Hashtable<T>::operator[]( const char* key_value ) const
{
    unsigned int h = hash(key_value);
    printf(\"Subscript const\\n\");
    P_Cell p = the_p_cell_vec[h];   
    while ( p != NULL )
    {
    if ( !strcmp(p->key_value, key_value) )
        return p->value;
    p = p->p_next;
    }   
    // Return default value of datatype
    return T_Default;
}

template <class T>
T& Hashtable<T>::operator[]( const char* key_value )
{
    unsigned int h = hash(key_value);
    P_Cell *p = &the_p_cell_vec[h];
    // Search the chain at position h in the vector
    while ( *p != NULL )
    {             
    if ( !strcmp((*p)->key_value, key_value) )
        return (*p)->value;
    p = &((*p)->p_next);
    }

    // Non existing key: create cell and chain in
    if ((*p = new Cell(key_value)) == NULL)
    {
        perror(\"Cannot allocate storage in hashtable\");
    exit(1);
    }
    count++;

    // It\'s the first insert
    if (first_cell == NULL)
    {
    first_cell = *p;
    last_insert_cell = *p;
    }
    else
    {
    // This one becomes the last inserted cell
    last_insert_cell->p_nextval = *p;
    last_insert_cell = *p;
    }
    return (*p)->value;       
}

template <class T>
const char* Hashtable<T>::First()
{
    // Point to first inserted value
    current_cell = first_cell;
    if (current_cell != NULL)
    return current_cell->key_value;
    return NULL;
}

template <class T>
const char* Hashtable<T>::First(T& val)
{
    // Point to first inserted value
    current_cell = first_cell;
    if (current_cell != NULL)
    {
    val = current_cell->value;
    return current_cell->key_value;
    }
    return NULL;
}

template <class T>
const char* Hashtable<T>::Next()
{
    // If not already at last cell
    if (current_cell != last_insert_cell)
    {
    // Move to next cell
    current_cell = current_cell->p_nextval;
    return current_cell->key_value;
    }
    return NULL;
}

template <class T>
const char* Hashtable<T>::Next(T& val)
{
    // If not already at last cell
    if (current_cell != last_insert_cell)
    {
    // Move to next cell
    current_cell = current_cell->p_nextval;
    val = current_cell->value;
    return current_cell->key_value;
    }
    return NULL;
}


template <class T>
void Hashtable<T>::list()
{
    int h = 0;
    while ( h < the_no_elements )
    {
    P_Cell *p = &the_p_cell_vec[h];
   
    // Print content of this chain
    printf(\" (%d)C %d\\t\", the_p_cell_vec[h], h);
    while ( *p != NULL )
    {
        printf(\" (%d) [%s]\", *p, (*p)->key_value);
        p = &((*p)->p_next);
    }
    printf(\"\\n\");
    h++;
    }
}

template <class T>
int Hashtable<T>::hash(const char* key)

    unsigned int i, h = 0, g;
    for (i=0; key[i] != 0; i++)
    {
    h = (h << 4) + key[i];
    if (g = h & 0xf0000000)
    {
        h ^= g >> 24;
        h ^= g;
    }
    }
    return h % the_no_elements;
}

#endif
Avatar billede stringbuffer Nybegynder
21. december 2001 - 15:13 #14
LOL kom til at bytte om på kommentarerne ved de 2 [] operatorer i hashtable.h da jeg byttede om på dem med cut\'n\'paste. Der skal selvfølgelig byttes om på kommentarerne :)
Anyway, koden bliver ikke anderledes af den grund.
Avatar billede stringbuffer Nybegynder
21. december 2001 - 16:26 #15
Jeg fandt noget som godt nok omhandler en anden form for operator, men da ligheden er pænt stor tror jeg at den kan bruges som forklaring:

T& operator*()
const T& operator*() const
const T& get() const

Provide access to the field\'s value.  To be sure a const function is called, it is best to use get(). For a non-const object, a non-const function is called, even if used as an rvalue.


Fundet på følgende URL:
http://aips2.nrao.edu/docs/aips/implement/Containers/RecordField.html#RecordFieldPtr%3Aoperator*()const
Avatar billede stringbuffer Nybegynder
21. december 2001 - 20:27 #16
Svaret er altså at man kan ikke bruge en const operator medmindre man opretter et const kopi af en eksisterende tabel og bruger indeks operatoren på den - så bliver dens const operator kaldt.

Man kan tilsyneladende ikke (desværre...) få compileren til selv at finde ud af hvornår den skal bruge hvilken version af operatoren, selvom det burde være nemt nok at lave en option til det... det er jo nemt at se hvad der er rvalue og hvad der er lvalue.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester