使用模板公开私有 Typedef

发布于 2024-10-28 03:22:39 字数 618 浏览 2 评论 0原文

我有一个包含私有 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 的大小或公开 现在什么是私有的?

I have a class that contains a private typedef and several member
functions:

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

Several member functions need to use m_fooPart in a similar way, so I
want to put that in a function. I put helper functions in the anonymous
namespace whenever I can, but in this case, they need to know what
FooPart is. So, I've done this:

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

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

By forcing the compiler to produce the FooPart type, am I still in the
land of well-defined behavior? Is there a more elegant way of
accomplishing this that doesn't increase the size of Foo or make public
what is now private?

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

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

发布评论

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

评论(3

初雪 2024-11-04 03:22:39

是的,这种方法会产生定义明确、符合标准的行为。

也就是说,向类添加成员函数不会增加类的大小(假设您指的是 sizeof 运算符的结果),所以我不确定您认为仅仅使辅助函数是 Foo 的私有成员。

Yes, that approach produces well-defined, standards-compliant behavior.

That said, adding member functions to a class does not increase the size of a class (assuming you mean the result of the sizeof operator), so I'm not sure what drawback you perceive in just making the helper function a private member of Foo.

攒一口袋星星 2024-11-04 03:22:39

简单的答案:公开 typedef。

这将泄漏实现的一个小细节(实际的内部类型),但因为它是类型定义的,所以您可以随时重新定义它,并且应该没问题。

稍微不那么简单:与辅助函数交朋友,提供对内部类型的访问。

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

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

您可以通过少量元编程来确保类型相同,使用 same_type 模板,如果两种类型相同,该模板将提供 true 值否则相同且错误。如果 typedef 仅在一处发生更改,静态断言将触发错误

再次回到简单:​​提供 typedef 或直接使用类型而不使用静态断言。

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

我会选择最后一个选项,虽然它可能看起来有点粗糙,不如其他选项精致,但事实是,这只是一个其他人没有使用的实现细节,你处于完整的状态对代码的控制,简单就好。

编辑,在评论之后。

我开始将其写为评论,但它变得太长,所以我将其添加到答案中。

该解决方案本身没有任何问题,除了您不必要地使函数通用并且将来的一些错误消息可能不像非通用签名那么简单。请注意,template不会公开typedef(如问题标题所示),而是会让编译器推断 调用处的类型。

如果您更改 typedef,则不会收到错误提示 helperFn 的参数无法与现有函数匹配,而是会推断类型并匹配函数,但是如果您使用不再存在的类型的属性,您将在 helperFn 中更深入地收到错误。或者更糟糕的是,如果类型的语义发生了变化,您甚至可能不会收到错误。

考虑一下 typedef 是一个 std::list,并且在函数中,您使用这个简单的正确 for 循环对其进行迭代:

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

您能捕捉到将 typedef 更改为 < 的效果吗? code>std::vector会有吗?即使代码现在不正确,编译器也不能。像这样编写 for 循环是否是一个好主意,或者为什么不只使用erase-remove习惯用法是不同的问题(事实上,前一个循环可以说更好< /em> 比列表的擦除删除),具体情况是语义已经改变,并且由于该类型在语法上与前一个类型兼容,编译器将没有注意到代码是错误的,它不会向您指出该函数,并且您很可能不会检查/重写它。

Simple answer: make the typedef public.

That will leak a minor detail of implementation (the actual internal type), but because it is typedefed you can redefine it at any time and it should be fine.

A little less simple: befriend the helper function, providing access to your internal type.

The problem with this second approach is that you are not only granting access to the typedef, but also to all the private parts of your class, and that might not be the best idea. At any rate, since this is an internal helper function, it is under your own control, and it should be fine. (Now that I think of it, you might want to declare the function in a named namespace, for the friend declaration to succeed)

Even less simple: Create a separate typedef inside the implementation file, and ensure that they are synchronized.

You can ensure that the types are the same with a small bit of metaprogramming, with a same_type<T,U> template that will provide a true value if the two types are the same and false otherwise. A static assert will trigger an error if the typedef changes in only one place

Back to simple again: provide the typedef or use the type directly without the static assert.

You are calling a function (this should not be a template as in your code) and passing a reference. If the typedef changes in the class, the call will fail and the compiler will tell you.

I would go for the last option, while it may look a little rough and less delicate than the others, the fact is that this is only an implementation detail that is not used by others, you are under full control of the code and well, simple is better.

EDIT, after the comment.

I started writing this as a comment, but it became too long, so I am adding it to the answer.

There is nothing wrong in that solution by itself, other than you are making a function generic unnecessarily and some error messages in the future might not be as simple as they could be with a non-generic signature. Note that the template will not expose the typedef (as the question title suggests) but rather it will make the compiler infer the type at the place of call.

If you change the typedef, instead of getting an error saying that the arguments to helperFn cannot be matched against the existing function, the type will be inferred and the function matched, but you will get an error deeper in helperFn if you use a property of the type that is no longer present. Or worse, you might not even get an error if it is the semantics of the type that have changed.

Consider that the typedef is of a std::list<X>, and that in the function you are iterating over it with this simple correct for loop:

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

Can you catch the effect that changing the typedef to std::vector<X> will have? The compiler cannot even if the code is now incorrect. Whether writing the for loop like that is a good idea, or why is it not just using the erase-remove idiom are different issues (as a matter of fact the previous loop is arguably better than erase-remove for a list), the concrete situation is that the semantics have changed, and because the type is syntactically compatible with the previous one the compiler will not notice that the code is wrong, it will not point you to that function and chances are that you will not review/rewrite it.

肩上的翅膀 2024-11-04 03:22:39

我想这就是泛型编程的思想——用Foo的一部分做一些事情而不知道它的类型。
一种更“传统”(强类型、无聊、可读、代码重复 - 你能想到的)方法是明确提及类型:

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

I guess this is the idea of generic programming - do stuff with a part of Foo without knowing its type.
A more "traditional" (strongly-typed, boring, readable, code-duplicating - you name it) way would be to mention the type explicitly:

int helperFn(const std::blahblah& foopart, int index)
{
    ...
    return foopart.fn(index);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文