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

问题描述 投票:0回答:6

使用其中一种比另一种有什么优势吗:

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。

c++ function constants function-qualifier
6个回答
20
投票

如果您必须创建一个与常量无关并避免重复的函数,一种巧妙的方法是将实现委托给模板,例如

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_cast
mutable
和其他在此类情况下尝试减少代码重复的东西相关的恐惧。如果出现错误,编译器会通知您。


9
投票

忽略是否真的需要 getter 的问题,在

const
和非
const
方法中复制功能时的最佳解决方案是让非
const
方法调用
const
方法并丢弃结果的
const
性(即您在问题中提出的两个选项中的第一个)。

原因很简单:如果你反过来做(使用非

const
方法中的逻辑),你可能会意外地修改一个
const
对象,并且编译器不会在编译时捕获它time (因为该方法未声明
const
) - 这将具有未定义的行为。

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

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


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_;
    }
}

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()];} };

我的观点是,理解问题并且不要过度设计。 
const_cast

或模板不需要解决这个问题。


下面完整的工作代码:

class Foo{ int* myIntP; int& get_impl() const{ return *myIntP; // even if Foo instance is const, *myInt is not } public: int& get(){return get_impl();} const int& get() const{return get_impl();} Foo() : myIntP(new int(0)){} ~Foo(){delete myIntP;} }; #include<cassert> int main(){ Foo f1; f1.get() = 5; assert( f1.get() == 5 ); Foo const f2; // f2.get() = 5; // compile error assert( f2.get() == 0 ); return 0; }



0
投票
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;
}

如果您确实
需要

修改非常量getter中的对象,那么无论如何您都无法拥有通用的实现...


-2
投票
通过非常量访问路径修改 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 在 
Effective C++

中的 Avoid Duplication in const 和 Non-

const
Member Function
 下推荐的。

© www.soinside.com 2019 - 2024. All rights reserved.