今天我了解了C ++“成员空间”的习惯用法,当T::bar
既是一个类型又是T.bar
时,它粗略地滥用了使T
和struct A {
struct Controls {
/* put some typedefs/data/functions here */
} Controls;
};
// Can be used as a type and value
A a;
A::Controls::iterator it = a.Controls.begin();
工作的C ++属性。在某种范围内的对象。
{{1}}
你有没有在练习中使用过这个成语?你觉得它有用吗?这个成语有什么好的或最好的应用?
答案 0 :(得分:6)
不,我从未使用过那种技术(我认为它不应该被称为“成语”):
由于我没有使用它,我发现它没有用。
该技术的一个很好的应用可能是混淆其他程序员。
另一个应用程序可能是写一篇技术喋喋不休的文章,说明对于一些想象中的实际遇到过的问题有多精彩,或许有很多模板元编程模糊不清?
Dunno,最好的应用程序可能是写一篇关于所有那些愚蠢规则的文章,比如你也可以在同一范围内有一个struct
和一个同名的函数,我记得,并指出通过远离语言的黑暗角落,可以更好地完成那些能够完成的任何事情。 :-)文章在金钱方面支付不多,但是他们付出了代价并且写作很有趣。请写下来(TIA)。
干杯&第h。,
答案 1 :(得分:2)
正如@Steve在评论中已经说过的那样,嵌套类型和这种类型的实例具有相同名称的事实并不是这种技术的核心方面。为了增加封装,我们甚至可以使用成员函数来访问实例(即使它看起来不像命名空间限定)。例如,你提供的例子可以改写如下,没有任何缺点(好吧,也许有,但我目前找不到任何一个):
struct A
{
struct Controls
{
//typedefs/data/functions
};
const Controls & getControls() { return controls_; }
private:
Controls controls_;
};
A a;
A::Controls::iterator = a.getControls().begin();
以下是我看到会员空间的方式。成员空间的目标是划分类的命名空间,以便将相关的typedef和方法组合在一起。上面的Controls
类可以在A
之外定义,但它与A
紧密相关(每个A
都与Controls
相关联-versa,Controls
只不过是包含它的对象A
上的一个视图)让它成为A
的成员感觉“很自然”,也可能与A
建立联系(如果需要访问A
的内部信息)。
因此,基本上成员空间允许我们在单个对象上定义一个或多个视图,而不会污染封闭的类命名空间。正如文章中所指出的,当您想要提供几种迭代类对象的方法时,这可能会非常有趣。
例如,假设我正在编写一个用于表示C ++类的类;我们称之为Class。 Class有一个名称,以及它所有基类的列表。为了方便起见,我想要一个Class来存储从它继承的所有类的列表(它的派生类)。所以我会有这样的代码:
class Class
{
string name_;
list< shared_ptr< Class > > baseClasses_;
list< shared_ptr< Class > > derivedClasses_;
};
现在,我需要一些成员函数来添加/删除基类/派生类:
class Class
{
public:
void addBaseClass( shared_ptr< Class > base );
void removeBaseClass( shared_ptr< Class > base );
void addDerivedClass( shared_ptr< Class > derived );
void removeDerivedClass( shared_ptr< Class > derived );
private:
//... same as before
};
有时,我可能需要添加迭代基类和派生类的方法:
class Class
{
public:
typedef list< shared_ptr< Class > >::const_iterator const_iterator;
const_iterator baseClassesBegin() const;
const_iterator baseClassesEnd() const;
const_iterator derivedClassesBegin() const;
const_iterator derivedClassesEnd() const;
//...same as before
};
我们正在处理的名称数量仍然可以管理,但是如果我们想要添加反向迭代呢?如果我们更改存储派生类的基础类型怎么办?那将添加另一堆typedef。此外,您可能已经注意到我们提供对开始和结束迭代器的访问的方式不遵循标准命名,这意味着我们不能使用依赖它的通用算法(例如Boost.Range)而无需额外的努力。
事实上,在查看成员函数名称时,我们使用前缀/后缀对它们进行逻辑分组是很明显的,我们试图避免使用名称空间。但由于我们不能在类中使用名称空间,我们需要使用技巧。
现在,使用成员空间,我们将所有与基础相关和派生相关的信息封装在它们自己的类中,这不仅让我们将相关的数据/操作组合在一起,而且还可以减少代码重复:因为操作基类的代码和派生类是一样的,我们甚至可以使用单个嵌套类:
class Class
{
struct ClassesContainer
{
typedef list< shared_ptr< Class > > const_iterator;
ClassesContainer( list< shared_ptr< Class > > & classes )
: classes_( classes )
{}
const_iterator begin() const { return classes_.begin(); }
const_iterator end() const { return classes_.end(); }
void add( shared_ptr< Class > someClass ) { classes_.push_back( someClass ); }
void remove( shared_ptr< Class > someClass ) { classes.erase( someClass ); }
private:
list< shared_ptr< Class > > & classes_;
};
public:
typedef ClassesContainer BaseClasses;
typedef ClassesContainer DerivedClasses;
// public member for simplicity; could be accessible through a function
BaseClasses baseClasses; // constructed with baseClasses_
DerivedClasses derivedClasses; // constructed with derivedClasses_
// ... same as before
};
现在我能做到:
Class c;
Class::DerivedClasses::const_iterator = c.derivedClasses.begin();
boost::algorithm::find( c.derivedClasses, & c );
...
在这个例子中,嵌套类没有与Class
耦合,因此它可以在外部定义,但你可以找到具有更强边界的例子。
好吧,在这篇长篇文章之后,我注意到我并没有真正回答你的问题:)。所以不,我从未在我的代码中实际使用成员空间,但我认为它有其应用程序。
我已经考虑了一两次,特别是当我为一个库编写一个Facade类时:Facade旨在通过一个入口点使库更容易使用,但结果它有几个成员函数,这些都是相关的,但具有不同程度的“相关性”。而且,它代表了一个对象集合,因此除了“面向功能”的成员函数之外,它还包含与迭代相关的typedef和成员函数。我考虑使用成员空间在逻辑“子空间”中划分类,以便拥有更清晰的界面。不知道为什么我没有这样做。
答案 2 :(得分:1)
不,我从未使用过它。
很好用吗?也许你可以用它来向你的同事们展示你们更好......就像一些人使用的模板一样,他们不应该为一个简单问题的复杂解决方案(注意模板,不像成员空间习语,有时是有用的)。
答案 3 :(得分:1)
无论是否可能令人困惑,任何语法灵活性始终受到欢迎。实际上这个技巧在boost.array中使用,这是一个最小的实现:
#include<cassert>
template<unsigned N>
struct fixed_array{ /* bla bla */
static unsigned size(){
return N;
}
};
int main(){
fixed_array<3> arr;
assert(arr.size() == 3); //like a stl container
assert(fixed_array<3>::size() == 3); //more proper, but less generic wrt stl containers
return 0;
}
因此,如果用户想要将静态成员函数/静态成员变量/嵌套类视为实例的属性而不是类,那么他/她可以。例如,编写通用代码很有用,在这个例子中根本不会混淆。