匿名名称空间中的私有静态成员函数或*函数?

时间:2022-10-27 07:43:25

I've recently made a change stylistically and wanted to see how other c++ programmers felt about it and if there were any downsides to it.

我最近在风格上做了一些改变,想看看其他c++程序员对它的看法,以及它是否有什么缺点。

Essentially, when I needed a utility function which doesn't need access to a given class member, what I used to do was something like this:

本质上,当我需要一个不需要访问给定类成员的实用函数时,我通常做的是这样的:

file.h

file.h

class A {
public:
    // public interface
private:
    static int some_function(int);
};

file.cpp

file.cpp

int A::some_function(int) {
    // ...
}

But more recently, I have been preferring to do something more like this:

但最近,我更喜欢做这样的事情:

file.cpp

file.cpp

namespace {
    int some_function(int) {

    }
}
// the rest of file.cpp

Here's my thought process:

这是我的思维过程:

  • it's one less file the edit
  • 它减少了一个文件的编辑
  • simply having the function be listed in the header file can hint at implementation details which have no need to be exposed (even if not publically available).
  • 只需在头文件中列出函数,就可以提示不需要公开的实现细节(即使不公开可用)。
  • if the prototype of the function needs to change, only one file needs to be recompiled.
  • 如果函数的原型需要更改,则只需要重新编译一个文件。

The last one is the most compelling to me. So my question is: are there any downsides to this?

最后一个是最吸引我的。所以我的问题是:这有什么缺点吗?

They are functionally equivalent for most purposes that I can think of. It kind of seems to me that a private static function can almost always be converted to a free function in an anonymous namespace.

就我所能想到的大多数目的而言,它们在功能上是等价的。在我看来,私有静态函数几乎总是可以在匿名名称空间中转换为*函数。

EDIT: One thing that comes to mind is that a private static function would have access to private members if given a pointer to an object to operate on, but if that's the case, why not make it a non-static member?

编辑:我想到的一件事是,如果给定一个指向对象的指针,一个私有的静态函数可以访问私有成员,但是如果是这样的话,为什么不让它成为一个非静态成员呢?

What do you guys think?

你们怎么看?

7 个解决方案

#1


21  

If the function is only used in one source file, it makes perfect sense to define it there. If nobody else is using it, it doesn't belong in the header.

如果该函数仅在一个源文件中使用,那么在那里定义它是很有意义的。如果没有其他人在使用它,它不属于标题。

As you say, it reduces dependencies and can potentially save some recompiles.

如您所言,它减少了依赖关系,并可能保存一些重新编译。

#2


7  

I started doing this several years ago and never found any real draw backs. Anything that the object uses, but doesn't itself change the state of the object and doesn't need to be used anywhere else, I like to put in an anonymous namespace in the implementation file.

我几年前就开始这样做了,但从来没有发现任何真正的倒退。对象使用的任何东西,但是本身不会改变对象的状态,也不需要在其他地方使用,我喜欢在实现文件中放入匿名名称空间。

#3


5  

Personally, the only static functions I use are factory functions, like this

就我个人而言,我使用的唯一静态函数是工厂函数,就像这样

class Angle {
public:
    static Angle FromDegree (float v);
    static Angle FromRadians (float v);

    ...
private:
    Angle (float degree);
};

Anything that does not need private access is made a free function. Anything that is not part of the public interface is, if possible, not put in a public place (conforming to your new approach).

任何不需要私有访问的内容都是一个*函数。如果可能的话,任何不属于公共接口的内容都不会放在公共位置(符合您的新方法)。

Scott Meyer's article "How Non-Member Functions Improve Encapsulation" roughly describes the same approach:

Scott Meyer的文章“非成员函数如何改进封装”大致描述了相同的方法:

Minimalness and Encapsulation

In Effective C++, I argued for class interfaces that are complete and minimal. Such interfaces allow class clients to do anything they might reasonably want to do, but classes contain no more member functions than are absolutely necessary. Adding functions beyond the minimum necessary to let clients get their jobs done, I wrote, decreases the class's comprehensibility and maintainability, plus it increases compilation times for clients. Jack Reeves has written that the addition of member functions beyond those truly required violates the open/closed principle, yields fat class interfaces, and ultimately leads to software rot. That's a fair number of arguments for minimizing the number of member functions in a class, but now we have an additional reason: failure to do so decreases a class's encapsulation.

在有效的c++中,我主张类接口是完整的、最小的。这样的接口允许类客户端做他们可能合理地想做的任何事情,但是类包含的成员函数并不比绝对必要的多。我写道,为让客户端完成他们的工作而添加超过最低限度的函数,降低了类的可理解性和可维护性,并增加了客户端的编译时间。Jack Reeves写了,添加成员函数之外真正需要违反合作原则打开/关闭,收益率脂肪类接口,并最终导致软件崩溃。这是一个公平的参数数量减少的数量在一个类的成员函数,但是现在我们有了一个额外的原因:未能这样做减少类的封装。

Of course, a minimal class interface is not necessarily the best interface. I remarked in Effective C++ that adding functions beyond those truly necessary may be justifiable if it significantly improves the performance of the class, makes the class easier to use, or prevents likely client errors. Based on his work with various string-like classes, Jack Reeves has observed that some functions just don't "feel" right when made non-members, even if they could be non-friend non-members. The "best" interface for a class can be found only by balancing many competing concerns, of which the degree of encapsulation is but one.

当然,最小的类接口不一定是最好的接口。我在有效的c++中说过,如果添加超出那些真正必要的函数,可以显著提高类的性能,使类更易于使用,或者防止可能的客户端错误,那么添加函数可能是合理的。根据Jack Reeves对各种字符串类类类的工作,他发现有些函数在成为非成员时并不会“感觉”正确,即使他们可能是非成员。一个类的“最佳”接口只能通过平衡许多相互竞争的关注点来找到,其中封装的程度只有一个。

Still, the lesson of this article should be clear. Conventional wisdom notwithstanding, use of non-friend non-member functions improves a class's encapsulation, and a preference for such functions over member functions makes it easier to design and develop classes with interfaces that are complete and minimal (or close to minimal). Arguments about the naturalness of the resulting calling syntax are generally unfounded, and adoption of a predilection for non-friend non-member functions leads to packaging strategies for a class's interface that minimize client compilation dependencies while maximizing the number of convenience functions available to them.

不过,这篇文章的教训应该是清楚的。尽管有传统的看法,使用非友非成员函数可以改进类的封装性,而对此类函数的偏爱可以使设计和开发具有完整和最小(或接近最小)接口的类更容易。关于生成的调用语法的自然性的争论通常是没有根据的,而采用对非成员函数的偏好会导致类接口的打包策略,从而最小化客户端编译依赖,同时最大化可用的方便函数的数量。

It's time to abandon the traditional, but inaccurate, ideas of what it means to be object-oriented. Are you a true encapsulation believer? If so, I know you'll embrace non-friend non-member functions with the fervor they deserve.

是时候抛弃传统的,但不准确的,关于面向对象的概念了。您是一个真正的封装信徒吗?如果是这样的话,我知道你会以他们应得的热情去拥抱非好友的非会员功能。

#4


4  

I can't see any downsides to using an anonymous namespace. If you can code the function usefully without access to the class members, then you should, because it decouples it from the class itself (like how the standard algorithms work on iterator pairs).

我看不出使用匿名名称空间有什么缺点。如果您可以在不访问类成员的情况下有效地编写函数,那么您应该这样做,因为它将函数与类本身分离(就像标准算法在迭代器对上的工作方式一样)。

#5


3  

Private static non-const variables are useless to any code not in the .cpp, so I always use an anonymous namespace like that.

私有静态非const变量对于任何不在.cpp中的代码都是无用的,所以我总是使用这样的匿名名称空间。

Static const variables may document constraints and expectations that are useful to client code, so they go in the header.

静态const变量可能会记录对客户端代码有用的约束和期望,因此它们会进入header。

Static functions may need access to private data members, so they go in the header if they have to.

静态函数可能需要访问私有数据成员,因此如果需要的话,它们将进入header。

#6


0  

If you're using a unity build (i.e. #including all the .cpp files of the project into one compilation unit in order to speed up compile times), you'll risk name collisions with other functions in anonymous namespaces.

如果您正在使用unity构建(例如,#将项目的所有.cpp文件包含到一个编译单元中,以加快编译时间),那么您可能会在匿名名称空间中与其他函数发生名称冲突。

That's about the only downside I've found.

这是我发现的唯一缺点。

#7


-1  

The whole reason for static function as opposed to global function is that it accesses the private members of the class. If this is not true, use a free function. This analysis is not complete answer until we have all 3 alternative ways to do it:

与全局函数相反,静态函数的整个原因是它访问类的私有成员。如果这不是真的,使用一个*函数。这个分析并不是完整的答案,除非我们有三种不同的方法:

free function:

免费的功能:

void f() { /* cannot access private members of any class */ }

static function:

静态函数:

class A {
public:
   static void g() { /* can access private members of class A */ }
};

friend functions:

朋友的功能:

    class A {
    public:
       friend void h();
    };
    class B {
    public:
       friend void h();
   };
   void h() { /* can access private members of both class A and class B */ }

The real problem with how c++ does this is that the function call looks different:

c++的真正问题在于函数调用看起来不同:

int main() {
   f();
   A::g();
   h();
 }

#1


21  

If the function is only used in one source file, it makes perfect sense to define it there. If nobody else is using it, it doesn't belong in the header.

如果该函数仅在一个源文件中使用,那么在那里定义它是很有意义的。如果没有其他人在使用它,它不属于标题。

As you say, it reduces dependencies and can potentially save some recompiles.

如您所言,它减少了依赖关系,并可能保存一些重新编译。

#2


7  

I started doing this several years ago and never found any real draw backs. Anything that the object uses, but doesn't itself change the state of the object and doesn't need to be used anywhere else, I like to put in an anonymous namespace in the implementation file.

我几年前就开始这样做了,但从来没有发现任何真正的倒退。对象使用的任何东西,但是本身不会改变对象的状态,也不需要在其他地方使用,我喜欢在实现文件中放入匿名名称空间。

#3


5  

Personally, the only static functions I use are factory functions, like this

就我个人而言,我使用的唯一静态函数是工厂函数,就像这样

class Angle {
public:
    static Angle FromDegree (float v);
    static Angle FromRadians (float v);

    ...
private:
    Angle (float degree);
};

Anything that does not need private access is made a free function. Anything that is not part of the public interface is, if possible, not put in a public place (conforming to your new approach).

任何不需要私有访问的内容都是一个*函数。如果可能的话,任何不属于公共接口的内容都不会放在公共位置(符合您的新方法)。

Scott Meyer's article "How Non-Member Functions Improve Encapsulation" roughly describes the same approach:

Scott Meyer的文章“非成员函数如何改进封装”大致描述了相同的方法:

Minimalness and Encapsulation

In Effective C++, I argued for class interfaces that are complete and minimal. Such interfaces allow class clients to do anything they might reasonably want to do, but classes contain no more member functions than are absolutely necessary. Adding functions beyond the minimum necessary to let clients get their jobs done, I wrote, decreases the class's comprehensibility and maintainability, plus it increases compilation times for clients. Jack Reeves has written that the addition of member functions beyond those truly required violates the open/closed principle, yields fat class interfaces, and ultimately leads to software rot. That's a fair number of arguments for minimizing the number of member functions in a class, but now we have an additional reason: failure to do so decreases a class's encapsulation.

在有效的c++中,我主张类接口是完整的、最小的。这样的接口允许类客户端做他们可能合理地想做的任何事情,但是类包含的成员函数并不比绝对必要的多。我写道,为让客户端完成他们的工作而添加超过最低限度的函数,降低了类的可理解性和可维护性,并增加了客户端的编译时间。Jack Reeves写了,添加成员函数之外真正需要违反合作原则打开/关闭,收益率脂肪类接口,并最终导致软件崩溃。这是一个公平的参数数量减少的数量在一个类的成员函数,但是现在我们有了一个额外的原因:未能这样做减少类的封装。

Of course, a minimal class interface is not necessarily the best interface. I remarked in Effective C++ that adding functions beyond those truly necessary may be justifiable if it significantly improves the performance of the class, makes the class easier to use, or prevents likely client errors. Based on his work with various string-like classes, Jack Reeves has observed that some functions just don't "feel" right when made non-members, even if they could be non-friend non-members. The "best" interface for a class can be found only by balancing many competing concerns, of which the degree of encapsulation is but one.

当然,最小的类接口不一定是最好的接口。我在有效的c++中说过,如果添加超出那些真正必要的函数,可以显著提高类的性能,使类更易于使用,或者防止可能的客户端错误,那么添加函数可能是合理的。根据Jack Reeves对各种字符串类类类的工作,他发现有些函数在成为非成员时并不会“感觉”正确,即使他们可能是非成员。一个类的“最佳”接口只能通过平衡许多相互竞争的关注点来找到,其中封装的程度只有一个。

Still, the lesson of this article should be clear. Conventional wisdom notwithstanding, use of non-friend non-member functions improves a class's encapsulation, and a preference for such functions over member functions makes it easier to design and develop classes with interfaces that are complete and minimal (or close to minimal). Arguments about the naturalness of the resulting calling syntax are generally unfounded, and adoption of a predilection for non-friend non-member functions leads to packaging strategies for a class's interface that minimize client compilation dependencies while maximizing the number of convenience functions available to them.

不过,这篇文章的教训应该是清楚的。尽管有传统的看法,使用非友非成员函数可以改进类的封装性,而对此类函数的偏爱可以使设计和开发具有完整和最小(或接近最小)接口的类更容易。关于生成的调用语法的自然性的争论通常是没有根据的,而采用对非成员函数的偏好会导致类接口的打包策略,从而最小化客户端编译依赖,同时最大化可用的方便函数的数量。

It's time to abandon the traditional, but inaccurate, ideas of what it means to be object-oriented. Are you a true encapsulation believer? If so, I know you'll embrace non-friend non-member functions with the fervor they deserve.

是时候抛弃传统的,但不准确的,关于面向对象的概念了。您是一个真正的封装信徒吗?如果是这样的话,我知道你会以他们应得的热情去拥抱非好友的非会员功能。

#4


4  

I can't see any downsides to using an anonymous namespace. If you can code the function usefully without access to the class members, then you should, because it decouples it from the class itself (like how the standard algorithms work on iterator pairs).

我看不出使用匿名名称空间有什么缺点。如果您可以在不访问类成员的情况下有效地编写函数,那么您应该这样做,因为它将函数与类本身分离(就像标准算法在迭代器对上的工作方式一样)。

#5


3  

Private static non-const variables are useless to any code not in the .cpp, so I always use an anonymous namespace like that.

私有静态非const变量对于任何不在.cpp中的代码都是无用的,所以我总是使用这样的匿名名称空间。

Static const variables may document constraints and expectations that are useful to client code, so they go in the header.

静态const变量可能会记录对客户端代码有用的约束和期望,因此它们会进入header。

Static functions may need access to private data members, so they go in the header if they have to.

静态函数可能需要访问私有数据成员,因此如果需要的话,它们将进入header。

#6


0  

If you're using a unity build (i.e. #including all the .cpp files of the project into one compilation unit in order to speed up compile times), you'll risk name collisions with other functions in anonymous namespaces.

如果您正在使用unity构建(例如,#将项目的所有.cpp文件包含到一个编译单元中,以加快编译时间),那么您可能会在匿名名称空间中与其他函数发生名称冲突。

That's about the only downside I've found.

这是我发现的唯一缺点。

#7


-1  

The whole reason for static function as opposed to global function is that it accesses the private members of the class. If this is not true, use a free function. This analysis is not complete answer until we have all 3 alternative ways to do it:

与全局函数相反,静态函数的整个原因是它访问类的私有成员。如果这不是真的,使用一个*函数。这个分析并不是完整的答案,除非我们有三种不同的方法:

free function:

免费的功能:

void f() { /* cannot access private members of any class */ }

static function:

静态函数:

class A {
public:
   static void g() { /* can access private members of class A */ }
};

friend functions:

朋友的功能:

    class A {
    public:
       friend void h();
    };
    class B {
    public:
       friend void h();
   };
   void h() { /* can access private members of both class A and class B */ }

The real problem with how c++ does this is that the function call looks different:

c++的真正问题在于函数调用看起来不同:

int main() {
   f();
   A::g();
   h();
 }