重复、常量和非常量吸气剂的优雅解决方案?

发布于 2024-07-20 16:11:51 字数 501 浏览 6 评论 0原文

当您拥有时,您不讨厌它吗?

class Foobar {
public:
    Something& getSomething(int index) {
        // big, non-trivial chunk of code...
        return something;
    }

    const Something& getSomething(int index) const {
        // big, non-trivial chunk of code...
        return something;
    }
}

我们无法使用另一种方法来实现这两种方法,因为您无法从 const 调用非 const 版本> 版本(编译器错误)。 从非 const 版本调用 const 版本需要进行强制转换。

是否有一个真正优雅的解决方案,如果没有,最接近的解决方案是什么?

Don't you hate it when you have

class Foobar {
public:
    Something& getSomething(int index) {
        // big, non-trivial chunk of code...
        return something;
    }

    const Something& getSomething(int index) const {
        // big, non-trivial chunk of code...
        return something;
    }
}

We can't implement either of this methods with the other one, because you can't call the non-const version from the const version (compiler error).
A cast will be required to call the const version from the non-const one.

Is there a real elegant solution to this, if not, what is the closest to one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

蓝礼 2024-07-27 16:11:51

我记得一本《Effective C++》书中提到,实现这一点的方法是通过放弃其他函数中的 const 来实现非常量版本。

它不是特别漂亮,但很安全。 由于调用它的成员函数是非常量的,因此对象本身也是非常量的,并且允许放弃常量。

class Foo
{
public:
    const int& get() const
    {
        //non-trivial work
        return foo;
    }

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

I recall from one of the Effective C++ books that the way to do it is to implement the non-const version by casting away the const from the other function.

It's not particularly pretty, but it is safe. Since the member function calling it is non-const, the object itself is non-const, and casting away the const is allowed.

class Foo
{
public:
    const int& get() const
    {
        //non-trivial work
        return foo;
    }

    int& get()
    {
        return const_cast<int&>(const_cast<const Foo*>(this)->get());
    }
};
微暖i 2024-07-27 16:11:51

怎么样:

template<typename IN, typename OUT>
OUT BigChunk(IN self, int index) {
    // big, non-trivial chunk of code...
    return something;
}

struct FooBar {
    Something &getSomething(int index) {
        return BigChunk<FooBar*, Something&>(this,index);
    }

    const Something &getSomething(int index) const {
        return BigChunk<const FooBar*, const Something&>(this,index);
    }
};

显然,您仍然会有目标代码重复,但没有源代码重复。 与 const_cast 方法不同,编译器将检查该方法的两个版本的 const 正确性。

您可能需要将 BigChunk 的两个有趣的实例声明为类的友元。 这是朋友的一个很好的用途,因为朋友功能隐藏在靠近朋友的地方,因此不存在无约束耦合的风险(噢!)。 但我现在不会尝试这样做的语法。 欢迎补充。

BigChunk 很可能需要尊重 self,在这种情况下,上述定义顺序不会很好地工作,并且需要一些前向声明来解决它。

另外,为了避免在标头中找到 BigChunk 并决定实例化并调用它,即使它在道德上是私有的,您可以将整个块移到 FooBar 的 cpp 文件中。 在匿名命名空间中。 具有内部联动。 还有一个牌子,上面写着“小心豹子”。

How about:

template<typename IN, typename OUT>
OUT BigChunk(IN self, int index) {
    // big, non-trivial chunk of code...
    return something;
}

struct FooBar {
    Something &getSomething(int index) {
        return BigChunk<FooBar*, Something&>(this,index);
    }

    const Something &getSomething(int index) const {
        return BigChunk<const FooBar*, const Something&>(this,index);
    }
};

Obviously you'll still have object code duplication, but no source code duplication. Unlike the const_cast approach, the compiler will check your const-correctness for both versions of the method.

You probably need to declare the two interesting instantiations of BigChunk as friends of the class. This is a good use of friend, since the friend functions are hidden away close to the friendee, so there is no risk of unconstrained coupling (ooh-er!). But I will not attempt the syntax for doing so right now. Feel free to add.

Chances are that BigChunk needs to deference self, in which case the above order of definition isn't going to work very well, and some forward declarations will be needed to sort it out.

Also, in order to avoid some numpty finding BigChunk in the header and deciding to instantiate and call it even though it's morally private, you can move the whole lot into the cpp file for FooBar. In an anonymous namespace. With internal linkage. And a sign saying "beware of the leopard".

疧_╮線 2024-07-27 16:11:51

我会将 const 转换为非常量(第二个选项)。

I would cast the const to the non-const (second option).

高冷爸爸 2024-07-27 16:11:51

对对象的 const 引用是有意义的(您限制了对该对象的只读访问),但如果您需要允许非 const 引用,您不妨将该成员公开。

我相信这是 Scott Meyers(高效 C++)。

The const reference to the object makes sense (you're putting a restriction on read-only access to that object), but if you need to allow a non-const reference, you might as well make the member public.

I believe this is a la Scott Meyers (Efficient C++).

擦肩而过的背影 2024-07-27 16:11:51

为什么不将公共代码提取到一个单独的私有函数中,然后让其他两个函数调用它呢?

Why not just pull the common code out into a separate, private function, and then have the other two call that?

吻风 2024-07-27 16:11:51

我敢建议使用预处理器:

#define ConstFunc(type_and_name, params, body) \
    const type_and_name params const body \
    type_and_name params body

class Something
{
};

class Foobar {
private:
    Something something;

public:
    #define getSomethingParams \
    ( \
        int index \
    )

    #define getSomethingBody \
    { \
        return something; \
    }

    ConstFunc(Something & getSomething, getSomethingParams, getSomethingBody)
};

I dare suggest using the preprocessor:

#define ConstFunc(type_and_name, params, body) \
    const type_and_name params const body \
    type_and_name params body

class Something
{
};

class Foobar {
private:
    Something something;

public:
    #define getSomethingParams \
    ( \
        int index \
    )

    #define getSomethingBody \
    { \
        return something; \
    }

    ConstFunc(Something & getSomething, getSomethingParams, getSomethingBody)
};
濫情▎り 2024-07-27 16:11:51

“const”这个概念的存在是有原因的。 对我来说,它建立了一个非常重要的合同,根据该合同编写程序的进一步说明。 但是您可以在以下几行上做一些事情: -

  1. 让您的成员“可变”
  2. 使“getters”常量
  3. 返回非常量引用

这样,如果您需要维护常量功能,则可以在 LHS 上使用常量引用正在使用 getter 和非常量用法(危险)。 但现在程序员有责任维护类不变量。

正如之前在 SO 中所说,放弃最初定义的 const 对象的常量并使用它是一个 UB 所以我不会使用强制转换。 同样将非常量对象设置为常量,然后再次抛弃常量,看起来不太好。

我在一些团队中看到的另一个编码指南是: -

  • 如果需要在类之外修改成员变量,则始终
    通过非常量成员函数返回指向它的指针。
  • 任何成员函数都不能返回非常量引用。 仅常量
    允许以 const 成员函数形式进行引用。

这使得整个代码库具有一定的一致性,并且调用者可以清楚地看到哪些调用可以修改成员变量。

The concept of 'const' is there for a reason. To me it establishes a very important contract based on which further instructions of a program are written. But you can do something on the following lines :-

  1. make your member 'mutable'
  2. make the 'getters' const
  3. return non-const reference

With this, one can use a const reference on the LHS if you need to maintain the const functionality where you are using the getter along with the non-const usage(dangerous). But the onus is now on the programmer to maintain class invariants.

As has been said in SO before, casting away constness of an originally defined const object and using it is an U.B. So i would not use casts. Also making a non-const object const and then again casting away constness would not look too good.

Another coding guideline that I have seen used in some teams is:-

  • If a member variable needs to be modified outside the class, always
    return a pointer to it via a non-const member function.
  • No member functions can return non-const references. Only const
    references are allowed form const member functions.

This allows some consistency in the overall codebase and the caller can clearly see which calls can modify the member variable.

挽梦忆笙歌 2024-07-27 16:11:51

尝试通过重构代码来消除吸气剂。 如果只有极少数其他事物需要 Something,请使用友元函数或类。

一般来说,Getters 和 Setters 会破坏封装性,因为数据是暴露给外界的。 使用友元只会将数据暴露给少数人,因此可以提供更好的封装。

当然,这并不总是可行,因此您可能会陷入吸气剂的困境。 至少,大部分或全部“重要的代码块”应该位于一个或多个私有函数中,由两个 getter 调用。

Try to eliminate the getters by refactoring your code. Use friend functions or classes if only a very small number of other things needs the Something.

In general, Getters and Setters break encapsulation because the data is exposed to the world. Using friend only exposes data to a select few, so gives better encapsulation.

Of course, this is not always possible so you may be stuck with the getters. At the very least, most or all of the "non-trivial chunk of code" should be in one or more private functions, called by both getters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文