C++作用域运算符分组?

3
有没有一种方法可以分组特定类的作用域方法,而不必每次都使用作用域运算符::?冒险在某些人中引起轻蔑,我可以将其粗略地类比为JavaScript的with语句;但是,这里它用于源代码,而不是执行。
一个简化的例子:想象一个Cheese类,其中weigh、shred和melt函数声明如下:
class Cheese {
    public:
        Cheese();
        ... // other constructors, copy, etc.
        ~Cheese();
        int weigh();
        int shred();
        int melt();
}

通常,函数定义如下所示:
Cheese::Cheese() { //constructor stuff };
... // other constructors, copy, etc.
Cheese::~Cheese() { //destructor stuff };
int Cheese::weigh() { return weighed; }
int Cheese::shred() { return shredded; }
int Cheese::melt() { return melted; }

有没有一种方法可以这样说,“嘿,编译器,所有这些定义的作用域都限定在Cheese类中。”

也许像这样?

scope::Cheese {
    Cheese() { //constructor stuff };
    ... // other constructors, copy, etc.
    ~Cheese() { //destructor stuff };
    int weigh() { return weighed; }
    int shred() { return shredded; }
    int melt() { return melted; }
}

或者,
Cheese:: {
    Cheese() { //constructor stuff };
    ... // other constructors, copy, etc.
    ~Cheese() { //destructor stuff };
    int weigh() { return weighed; }
    int shred() { return shredded; }
    int melt() { return melted; }
}

不行。即使没有,也不确定如何处理模板化的Cheese。 - xaxxon
2
就我个人而言,我认为这是C++中最毫无意义和恼人的方面之一。 - Kyle Strand
@xaxxon 为什么模板会成为问题? - Kyle Strand
也许不是那样,但通过说出ClassName::method_name来搜索定义的能力确实非常方便——比不再输入类名要重要得多... - xaxxon
1
您仍然可以在建议的系统中进行搜索。您将在第一个命题中遇到“scope :: Cheese”语句;在第二个命题中则是“Cheese ::”文本。 kjm - kmiklas
如果你认为好处超过了当前系统的烦恼,那么就将其作为编码最佳实践准则,始终使用显式作用域。相反,在我的代码(以及我所使用的大多数代码)中,声明在Foo.hpp中的方法总是在Foo.(something)中定义的,因此搜索问题并不大。 - Kyle Strand
2个回答

2

除了在函数定义块本身中定义方法(这有各种潜在的缺点)之外,没有任何其他方法可以做到这一点。

至少部分原因是为了确保类与命名空间不同,不能“重新打开”以添加其他成员。


“no”并不是一个真正的高质量回答,它更像是一条评论。 - xaxxon
@xaxxon 但这是正确的答案。还有什么好说的呢? - Kyle Strand
请进一步解释您所说的“重新打开”以添加其他成员的含义。 - kmiklas
@kmiklas 在这里可以看到一个例子(http://coliru.stacked-crooked.com/a/9c842cf4e9a9a258)。重点是类定义块只能在翻译单元中看到一次,而在代码中的不同点(甚至在不同的头文件中)可能有多个块定义成员。换句话说,命名空间定义可以“重新打开”。这就是为什么`std::`的内容取决于您包含哪些头文件。在某些语言中,例如Ruby,类*可以*被重新打开,以便添加其他成员。 - Kyle Strand

0
你可以:
  • 在类定义中完全定义方法;或者

  • 除构造函数和析构函数外,使用typedef短类名称来实现。

还有一些不太实用的可能性,包括源代码预处理。


例子:

class Cheese
{
public:
    Cheese();
    //... // other constructors, copy, etc.
    ~Cheese();
    auto weight() -> int;
    auto shred() -> int;
    auto melt() -> int;
};

using X = Cheese;

Cheese::Cheese() {}
Cheese::~Cheese() {}

auto X::weight() -> int { return 0; }
auto X::shred() -> int  { return 0; }
auto X::melt() -> int   { return 0; }

auto main() -> int
{
    return Cheese().weight();
}

个人而言,我希望更多的库只有头文件,并且方法定义在类定义中。仅使用头文件的方法无法轻松处理模块交叉依赖性,其中X的实现取决于Y的接口,反之亦然,在当前1960年代的构建技术下,这可能会导致不切实际的长时间构建。但是,在后者成为问题的情况下,可以通过增加更多硬件来解决,而幸运的是,第一个问题很少出现。

1
我不确定X::Cheese::更好 - 至少在我看来,显式作用域语法中最丑陋的部分是::。也许可以使用类似Rust的语法(即为了让喜欢C++的同事发疯)在文件末尾使用#undef impl,并在文件开头使用#define impl auto Cheese:: - Kyle Strand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接