Const函数调用非const,反之亦然(以避免重复)?

时间:2017-06-26 08:23:17

标签: c++ function const

使用一个优于另一个是否有任何优势:

class Foo
{
public:
    const int& get() const
    {
        // stuff here
        return myInt;
    }

    int& get()
    {
        return const_cast<int&>(static_cast<const Foo*>(this)->get());
    }
};

或者

class Foo
{
public:
    int& get()
    {
        // stuff here
        return myInt;
    }

    const int& get() const
    {
        return const_cast<Foo*>(this)->get();
    }
};

我只使用了第一个,但我刚看到第二个用在某个地方,所以我很想知道。

注释// stuff here可能是一个非平凡的检查,例如检索表的索引以便返回表的成员的引用(例如:myInt = myTable[myComputedIndex];)所以我不能只是把它公之于众。因此,表和任何成员都不是const。

6 个答案:

答案 0 :(得分:20)

如果你必须创建一个与const-anostic不相关的函数,并避免重复,一个巧妙的方法是将实现委托给模板,例如

class Foo {
private: 

    int my_int;
    template <typename ThisPtr>
    static auto& get(ThisPtr this_ptr) { 
        return this_ptr->my_int;
    }

public:
    int& get() {
        return get(this);
    }

    const int& get() const {
        return get(this);
    }
};

通过这种方式,您可以避免使用const_castmutable以及在此类情况下尝试减少代码重复的其他内容。如果您遇到问题,编译器会通知您。

答案 1 :(得分:9)

忽略您是否确实需要getter的问题,在const和非const方法中复制功能时的最佳解决方案是使用非const方法调用const方法并抛弃结果的const - 即你在问题中提出的两个替代方案中的第一个。

原因很简单:如果你这样做(使用非const方法中的逻辑),你可能会意外地最终修改const对象,并且编译器赢了在编译时捕获它(因为该方法未声明const) - 这将具有未定义的行为。

当然,如果“getter”实际上不是一个getter(例如,如果它做的事情比仅返回对私有字段的引用更复杂的话),这只是一个问题。

此外,如果您不受C ++ 11约束,Curious在其答案中提供的基于模板的解决方案是避免此问题的另一种方法。

答案 2 :(得分:2)

  

使用一个优于另一个是否有任何优势:...

不,两者都不好,因为它们违反了数据封装原则。

在您的示例中,您应该将myInt作为公共成员。 对这种情况有吸引力是没有优势的。

如果你真的想要(需要)getter和setter函数,它们应该是这样的:

class Foo
{
private: 
    mutable int myInt_;
 // ^^^^^^^ Allows lazy initialization from within the const getter,
 //         simply omit that if you dont need it.

public:
    void myInt(int value)
    {
        // Do other stuff ...
        myInt = value;
        // Do more stuff ...
    }

    const int& myInt() const
    {
        // Do other stuff ...
        return myInt_;
    }
}

答案 3 :(得分:1)

您没有说myInt来自哪里,最佳答案取决于。 有2 + 1种可能的情况:

1)最常见的情况是myInt来自类内部的指针

假设这是避免代码重复和转换的最佳解决方案。

class Foo{
    int* myIntP;
    ... 
    int& get_impl() const{
       ... lots of code
       return *myIntP; // even if Foo instance is const, *myInt is not
    }
public:
    int& get(){return get_impl();}
    const int& get() const{return get_impl();}
};

上述情况适用于指针数组和(大多数)智能指针。

2)另一个常见的情况是myInt 是引用或值成员,那么之前的解决方案不起作用。 但是根本不需要getter的情况也是如此。 在这种情况下不要使用吸气剂。

class Foo{
     public:
     int myInt; // or int& myInt;
};

完成! :)

3)@Aconcagua指出的第三种情况是内部固定数组。在这种情况下,这是一个折腾,它真的取决于你在做什么,如果找到索引确实是问题,那么可以将其考虑在内。然而,目前尚不清楚申请是什么:

class Foo{
    int myInts[32];
    ... 
    int complicated_index() const{...long code...}
public:
    int& get(){return myInts[complicated_index()];}
    const int& get() const{return myInts[complicated_index()];}
};

我的观点是,了解问题并且不要过度工程。 <{1}}或模板不需要解决此问题。

以下完整的工作代码:

const_cast

答案 4 :(得分:0)

当您打算访问更复杂的内部结构时(通过编辑澄清;例如为std :: vector做内部数组提供operator[](size_t index)),那么您必须确保不这样做通过修改可能的const对象来调用未定义的行为。

在第二种方法中,这样做的风险更高:

int& get()
{
    // stuff here: if you modify the object, the compiler won't warn you!
    // but you will modify a const object, if the other getter is called on one!!!
    return myInt;
}

在第一个版本中,你是安全的(除非你在这里做const_cast,现在真的很糟糕......),这是 这种方法的优点:

const int& get() const
{
    // stuff here: you cannot modify by accident...
    // if you try, the compiler will complain about
    return myInt;
}

如果您实际上需要来修改非const getter中的对象,那么无论如何都不能有一个通用的实现......

答案 5 :(得分:-2)

  

通过非const访问路径[...]修改const对象会导致未定义的行为。

(资料来源:http://en.cppreference.com/w/cpp/language/const_cast

这意味着如果myInt实际上是Foo的const成员,则第一个版本可能导致未定义的行为:

class Foo
{
    int const myInt;
public:
    const int& get() const
    {
        return myInt;
    }
    int& get()
    {
        return const_cast<int&>(static_cast<const Foo*>(this)->get());
    }
};
int main()
{
    Foo f;
    f.get() = 10; // this compiles, but it is undefined behavior
}

第二个版本无法编译,因为get的非常量版本格式不正确:

class Foo
{
    int const myInt;
public:
    int& get()
    {
        return myInt;
        // this will not compile, you cannot return a const member
        // from a non-const member function
    }
    const int& get() const
    {
        return const_cast<Foo*>(this)->get();
    }
};
int main()
{
    Foo f;
    f.get() = 10;  // get() is ill-formed, so this does not compile
}

此版本实际上是由Scott Meyers在有效C ++ 避免在const和非const成员函数中重复使用。