防止为非const对象调用const函数

时间:2019-05-14 19:51:46

标签: c++ c++11

以下代码包含operator()的const和非const版本。 It outputs

  

非常数运算符,false
  const op,true
  const op,true
  const op,是的

即如果类型S的对象是const或提交的指针是const-行// 2// 3// 4,则调用const版本。现在,我希望第// 2行中的代码导致编译时错误,即,我希望const版本只能在const对象上调用。显然,static_assert上的is_const_v<decltype(*this)>无法正常工作。还有其他想法吗?
我知道,将非常量变量转换为常量很容易。但这至少会使滥用显而易见。

#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
}

修改
我的问题背后的原因如下。我正在存储提交的指针。这需要丢弃提交的指针的常量性。现在,我想防止const数据被错误地修改。

3 个答案:

答案 0 :(得分:7)

您可以明确删除以下版本

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

禁止

s1( &i2 ); // 2

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

禁止

s2( &i1 ); // 3

答案 1 :(得分:2)

让我们看看如果将this设为显式参数(虚构语法),它将是非成员函数将如何工作:

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)> >
}

解决方案是删除参数之间具有不同常数的函数:

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;

现在可以使用成员函数完成同样的操作

struct S {
    void operator()(int*) {}
    void operator()(int const*) const {}
    void operator()(int const*) = delete;
    void operator()(int*) const = delete;
};

答案 2 :(得分:1)

您可以通过删除带有const int*的版本来防止调用该函数。例如:

#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)
}