Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent const function being called for non-const object

Tags:

c++

c++11

The following code contains a const and a non-const version of operator(). It outputs

Non-const op, false
Const op, true
Const op, true
Const op, true

I.e. the const version is called if either the object of type S is const or if the submitted pointer is const - lines // 2, // 3, // 4. Now I want the code in line // 2 to result in a compile time error, i.e. I want the const version to be callable only on const objects. Obviously a static_assert on is_const_v<decltype(*this)> will not work. Any other ideas?
I know, it is easy to cast a non-const variable to a const one. But that would make the misuse at least obvious.

#include <iostream>
#include <type_traits>

struct S
{
    void
    operator()( int * )
    {
        std::cout << std::boolalpha
            << "Non-const op, "
            << std::is_const_v<typename std::remove_reference_t<decltype(*this)> > << '\n';
    }
    void
    operator()( int const * ) const
    {
        std::cout << std::boolalpha
            << "Const op, "
            << std::is_const_v<typename std::remove_reference_t<decltype(*this)> > << '\n';
    }
};

int main()
{
    S         s1;
    S const   s2;
    int       i1= 0;
    int const i2= 1;

    s1( &i1 ); // 1
    s1( &i2 ); // 2
    s2( &i1 ); // 3
    s2( &i2 ); // 4
}

Edit
The reasoning behind my question is as follows. I am storing the submitted pointer. This requires casting away constness of the submitted pointer. Now I want to prevent const data being modified erroneously.

like image 906
Claas Bontus Avatar asked May 14 '19 19:05

Claas Bontus


3 Answers

You can explicitly delete the following version

void operator()( int const * ) = delete;

to disallow

s1( &i2 ); // 2

and

void operator()( int * ) const = delete;

to disallow

s2( &i1 ); // 3
like image 90
R Sahu Avatar answered Oct 17 '22 15:10

R Sahu


Let's see how it would work if it would be non member function, if we make this an explicit parameter (imaginary syntax):

void call(S* this, int*) {
    // Wha? we clearly know `S*` is not const
    std::is_const_v<typename std::remove_reference_t<decltype(*this)> >
}

void call(S const* this, int const*) {
    // same
    std::is_const_v<typename std::remove_reference_t<decltype(*this)> >
}

The soluton would be to delete functions with a different constness between parameters:

void call(S* this, int*) {}
void call(S const* this, int const*) {}
void call(S* this, int const*) = delete;
void call(S const* this, int*) = delete;

Now the same can be done with member function:

struct S {
    void operator()(int*) {}
    void operator()(int const*) const {}
    void operator()(int const*) = delete;
    void operator()(int*) const = delete;
};
like image 45
Guillaume Racicot Avatar answered Oct 17 '22 15:10

Guillaume Racicot


You can prevent the function from being called by deleting the version that takes a const int*. For example:

#include <iostream>

struct S {
    void foo(int*) {
        std::cout << "Calling foo(int*)\n"; 
    }
    void foo(int const*) const {
        std::cout << "Calling foo(int const*)\n"; 
    }
    void foo(int const*) = delete; 
};

int main() {
    S s1;
    S const s2;
    int i1;
    int const i2;

    s1.foo(&i1); // This compiles fine (both mutable)
    s2.foo(&i2); // This compiles fine too (both const)
    s2.foo(&i1); // This also compiles (s2 const, i1 mutable)

    s1.foo(&i2); // This fails as intended (s1 mutable, i2 const)
}
like image 1
Alecto Irene Perez Avatar answered Oct 17 '22 15:10

Alecto Irene Perez