Domanda

Is there a way in C++03 (or earlier) to write a class that can either store a const or non-const pointer, and handles access appropriately? Take the usage of the non-functional "SometimesConst" class as an example:

class SometimesConst
{
    public:
    SometimesConst(int * buffer) : buffer(buffer) {} // Needs const qualifier?

    int* get() { return buffer; } // Needs const qualifier?

    void increment() { counter++; }

    private:
    int * buffer; // Needs const qualifier?
    int counter;
};

void function(int * n, const int * c)
{
    // These are both okay
    SometimesConst wn(n);
    SometimesConst wc(c);

    // Reading the value is always allowed
    printf("%d %d", wn.get()[0], wc.get()[0]);

    // Can increment either object's counter
    wn.increment();
    wc.increment();

    // Can set non-const pointer
    wn.get()[0] = 5;

    // Should generate a compiler error
    wc.get()[0] = 5;
}

Creating a const SometimesConst would not allow modification of the counter property of the object. Can a class be designed that has compile-time const safety for input objects, only if they are passed in as const?

È stato utile?

Soluzione

No, not the way you are wanting to use it. The only way to have different behavior at compile time is to have different types. However, you can make that fairly easy to use:

#include <stdio.h>

template <typename T>
class SometimesConst
{
  public:
    SometimesConst(T* buffer) : buffer(buffer) { }
    T* get() { return buffer; }
    void increment() { ++counter; }
  private:
    T *buffer;
    int counter;
};

typedef SometimesConst<const int> IsConst;
typedef SometimesConst<int> IsNotConst;

void function(int * n, const int * c)
{
  IsNotConst wn(n);
  IsConst wc(c);

  // Reading the value is always allowed
  printf("%d %d", wn.get()[0], wc.get()[0]);

  // Can increment either object's counter
  wn.increment();
  wc.increment();

  // Can set non-const pointer
  wn.get()[0] = 5;

  // Should generate a compiler error
  wc.get()[0] = 5;
}

Altri suggerimenti

The language already mostly lets you do this with a simple class; with the way const cascades to access to members (combined with mutable for the counter member, which you've indicated should always be mutable), you can provide both read-only and read-write access to a buffer quite easily:

class C
{
public:
    C(int* buffer) : buffer(buffer) {}

    const int* get() const { return buffer; }
    int* get() { return buffer; }

    void increment() const { counter++; }

private:
    int* buffer;
    mutable int counter;
};

void function(int* n)
{
    // These are both okay
    C wn(n);
    const C wc(n);

    // Reading the value is always allowed
    printf("%d %d", wn.get()[0], wc.get()[0]);

    // Can increment either object's counter
    wn.increment();
    wc.increment();

    // Can set non-const pointer
    wn.get()[0] = 5;

    // Generates a compiler error
    wc.get()[0] = 5;
}

What you can't do with this is neatly arrange for the class to be instantiated with either a int* or a const int*; the two lead to totally different semantics for your class, so you should split it into two if you really need that.

Fortunately, templates make this easy:

template <typename T>
class C
{
public:
    C(T* buffer) : buffer(buffer) {}

    const T* get() const { return buffer; }
    T* get() { return buffer; }

    void increment() const { counter++; }

private:
    T* buffer;
    mutable int counter;
};

Now a C<int> is as above, but a C<const int> only provides read-only access to the buffer, even when the C<const int> object itself is not marked as const:

void function(int* n1, const int* n2)
{
    C<int>             a(n1);
    C<const int>       b(n2);
    const C<int>       c(n1);
    const C<const int> d(n2);

    // Reading the value is always allowed
    printf("%d %d %d %d",
       a.get()[0], b.get()[0],
       c.get()[0], d.get()[0]
    );

    // Incrementing the counter is always allowed
    a.increment();
    b.increment();
    c.increment();
    d.increment();

    // Can set non-const pointer
    a.get()[0] = 5;

    // Cannot set const pointer, or const/non-const pointer behind const object
    //b.get()[0] = 5;
    //c.get()[0] = 5;
    //d.get()[0] = 5;
}

Live demo

I think that there is a design problem if you want to store two different things which must be handled in different ways in one class. But yes, you can do it:

struct X{};

class A
{   
    public:
        A(const X*) { cout << "const" << endl; }
        A(X*)       { cout << "non const" << endl; }

};  

int main()
{   
    const X x1; 
    X x2; 

    A a1(&x1);
    A a2(&x2);
}   

the output is expected:

const
non const
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top