2011-03-30 92 views
4

我有一个包含私有的typedef,和几个成员 函数的类:使用模板暴露私有的typedef,

class Foo 
{ 
private: 
    typedef std::blahblah FooPart; 
    FooPart m_fooPart; 
    ... 
public: 
    int someFn1(); 
    int someFn2(); 
}; 

几个成员函数需要以类似的方式使用m_fooPart,所以我 要把它放在一个函数中。我尽可能在匿名的 名称空间中放置助手函数,但在这种情况下,他们需要知道FooPart是什么。在 土地的良好定义的行为仍然

namespace 
{ 
    template <typename T> 
    int helperFn(const T& foopart, int index) 
    { 
    ... 
    return foopart.fn(index); 
    } 
} 

int Foo::someFn1() 
{ 
    ... 
    return helperFn(m_fooPart, ix);  
} 

通过迫使编译器生成的FooPart类型,我在:所以,我已经做到了这一点?有没有更优雅的方式 完成这个不增加Foo的大小或公开 什么是私人的?

+3

为什么不把辅助函数的Foo'的私有成员'?你认为采取这种方法会失去什么? – ildjarn 2011-03-30 20:28:48

+0

准确地说,模板将* not *暴露'typedef',而是让编译器在调用的地方解析类型。 – 2011-03-31 07:41:01

+0

@dribeas,谢谢。这似乎有点奇怪,类型可以隐式地(由编译器)解决,但不是明确地(由我)解决。 – criddell 2011-04-01 13:02:15

回答

4

是的,该方法可以产生明确定义的符合标准的行为。

也就是说,向类中添加成员函数并不会增加类的大小(假设您的意思是sizeof运算符的结果),所以我不确定您在创建助手函数时感觉到什么缺点私人成员Foo

+0

我猜这里的_size_的意思是“.h文件中的字符数” – anatolyg 2011-03-30 20:42:27

+2

@anatolyg:也许,但我不希望 - 这是一个非常愚蠢的指标。 – ildjarn 2011-03-30 20:43:46

+0

我说“大小”,但我真的应该说“复杂性”。主要是保持类声明尽可能简单。 – criddell 2011-03-31 03:09:09

3

简单的回答:使typedef公开。

这会泄漏实现的一个小细节(实际的内部类型),但是因为它是typedefed,你可以在任何时候重新定义它,它应该没问题。

简单一点:交友助手功能,提供访问您的内部类型。

第二种方法的问题在于,您不仅授予对typedef的访问权限,还授予对您班级的所有私有部分的访问权限,这可能不是最好的主意。无论如何,因为这是一个内部帮助函数,所以它是在你自己的控制之下的,它应该没问题。 (现在我想起来了,你可能想声明函数在一个命名的命名空间中,以使friend声明成功)

更简单:在实现文件中创建一个单独的typedef,并确保它们是同步的。

您可以确保类型与一小段元编程相同,same_type<T,U>模板将提供一个true值(如果两个类型相同,否则为false)。如果typedef只在一个地方更改,则静态声明将触发错误

返回简单:提供typedef或直接使用类型,而不使用静态声明。

你正在调用一个函数(这不应该是你的代码中的模板),并传递一个引用。如果typedef在类中发生更改,则调用将失败,编译器会告诉您。

我会去的最后一个选项,虽然它可能看起来有点粗糙,少细腻比别人,但事实是,这是唯一没有被其他人一个实现细节,你完全控制之下的代码和好,简单就更好。

编辑,之后的评论。

我开始写这个作为评论,但它变得太长了,所以我将它添加到答案。

这个解决方案本身并没有什么错,除了不必要的做一个通用函数,以后一些错误信息可能不会像使用非通用签名那样简单。请注意,template而不是公开typedef(问题标题建议),而是它会使编译器推断在调用的地方的类型。

如果更改typedef,而不是得到一个错误,指出该参数helperFn不能对现有的功能相匹配,则类型推断,功能匹配,但你会在helperFn得到一个错误更深,如果你使用不再存在的类型的属性。或者更糟糕的是,如果这种类型的语义发生了变化,那么您甚至可能不会收到错误。

认为typedef的是std::list<X>的,而且在你迭代它与这个简单的正确的for循环的功能:

for (typename T::iterator it=x.begin(), end=x.end(); it != end;) { 
    if (condition(*it)) 
     it = x.erase(it); 
    else 
     ++it; 
} 

你能赶上改变的typedef std::vector<X>就会有效果?即使代码现在不正确,编译器也不能。是否这样写for for循环是一个好主意,或者为什么它不仅仅使用擦除 - 删除成语是不同的问题(事实上,前一个循环是可以说是更好的擦除删除一个列表),具体情况是语义已经改变,并且因为类型在语法上与前一个兼容,所以兼容编译器不会注意到代码是错误的,它不会指向你的那个函数,并且机会是你不会审查/重写它。

+0

首先,作为模板的函数有什么问题?其次,关于如果我的typedef更改,通知编译器的好处。我开始认为你的最后一个选择是顺其自然的方式(@anatolyg给了我同样的建议)。 – criddell 2011-03-31 03:14:43

+0

@criddell:我已经更新了答案,并对您的方法的问题进行了一些具体评论。 – 2011-03-31 08:02:01

1

我想这是通用编程的想法 - 做不知道它的类型的部分Foo的东西。 一个更“传统”(强类型的,无聊的,可读的代码复制 - 你的名字)的方法是明确提及类型:

int helperFn(const std::blahblah& foopart, int index) 
{ 
    ... 
    return foopart.fn(index); 
} 
+0

实际类型非常多,时间更长。我使用了一个typedef来避免代码重复(如你所提到的),并为该类型提供一个描述性名称。 – criddell 2011-03-31 03:11:44