头文件和源文件中的 typedef 返回类型 - 最佳实践是啥?

Posted

技术标签:

【中文标题】头文件和源文件中的 typedef 返回类型 - 最佳实践是啥?【英文标题】:typedef return type in header and source files - what is best practice?头文件和源文件中的 typedef 返回类型 - 最佳实践是什么? 【发布时间】:2021-01-24 05:57:21 【问题描述】:

我有一个具有长返回类型的成员函数的类:

/* MyClass.hpp */
namespace foo 

  class MyClass
  
  public:
    SomeNameSpace::Lengthy_return_type_name someFunction(params...); 
  ;

我想让它更具可读性,而不会让读者感到困惑,而且我不一定想将 typedef 暴露给外界。所以在 MyClass.hpp 中,我在 MyClass 中添加了以下 typedef:

/* MyClass.hpp */
namespace foo 

  class MyClass
  
  private:
    typedef SomeNameSpace::Lengthy_return_type_name myType;
  public:
    myType someFunction(params...); 
  ;

现在,在 MyClass.cpp 中,我遇到了一个难题,因为 myType 不可见,无法用作返回类型。如果我公开了 typedef,我可以使用 MyClass::myType,但我不喜欢这样,因为我认为它令人困惑并且还暴露了 myType。我也可以将 typedef 添加到 MyClass.cpp 的顶部,但这可能会失去意义,因为读者必须进行一些挖掘才能找出类型所指的内容。

我是不是想多了?在这种情况下,最佳做法是什么?

【问题讨论】:

从技术上讲,您可以使用尾随返回类型:auto MyClass::someFunction(...) -> myType “现在,在 MyClass.cpp 中我遇到了一个难题,因为 myType 不可见,无法用作返回类型”您为什么认为这会是个问题?公共方法的返回类型对外可见的,typedef是否是私有的并不重要 @idclev463035818,奇怪的是,它实际上是compiles。 :) 那很奇怪。我猜应该有一些规则。 【参考方案1】:

简短的回答是:公开别名。没有理由将其设为私有。

为了说明,我将您的示例更进一步。假设myType 不仅仅是一个别名,而是一个新类型:

struct foo 
    private:
       struct myType ;
    public:
       myType someFunction();
;

现在让我们看看这会带来什么:myType 真的是私人的吗?没有。

调用者可以做的事

 foo f;
 auto x = f.someFunction();
 using myMyType = decltype(x);

Tada,用户有一个名为myType 的别名myMyType。公共函数的返回类型不是私有的!只有名称 foo::myType 是私有的,但不同名称引用的类型很常见。试图隐藏这个名字是没有意义的。您唯一能做到的就是强制用户使用不同的名称。

还要考虑人们可能期望的定义

 foo::myType foo::someFunction() 

编译失败,因为foo::myType 是在foo 的私​​有部分中定义的。但是,公共方法的返回类型不能是真正私有的。如果不允许上面的定义,你可以写:

 decltype( declval<foo>().someFunction() ) foo::someFunction() 

因此,不允许上述定义确实没有意义。

Live example

【讨论】:

为什么foo::myType foo::someFunction() 编译,但从语言律师的角度来看foo::myType someFunction() 失败? @Evg 我不得不承认这是我论证的弱点。我是一个非常糟糕的语言律师。我不能告诉你究竟是什么使一个编译而另一个不编译。考虑foo 可能有一个私有嵌套类myType2 不会通过公共接口泄漏,那么foo::myType2 someFreeFunction() 不可能是正确的 @Evg 我猜答案就在这里:en.cppreference.com/w/cpp/language/unqualified_lookup @Evg 巧合的是刚刚弹出了一个有点相关的问题:***.com/q/64275733/4117728 @TanveerBadar,没错!问题是一些标准的是什么。【参考方案2】:

肯定是想多了,因为无论如何你的调用者都需要有完整的定义。限制对他们必须知道的事情的访问是没有意义的。

此外,当 typedef 用作返回类型时,向调用者隐藏 typedef 有什么意义?它不会给你买任何东西,在这种特殊情况下不涉及封装。

最后,我可以建议切换到现代语法来做同样的事情吗?

using myType = SomeNameSpace::Lengthy_return_type_name;

你最终会喜欢上它,因为它提供的不仅仅是简单的 typedef,例如alias templates.

【讨论】:

【参考方案3】:

在开头定义所有类型是个好主意。

看看标准库的任何类实现:vector 的例子。 “成员类型部分”由有用的 typedef 填充。

如果看实现细节,大致的方案是:

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>


  typedef _Vector_base<_Tp, _Alloc>         _Base;
  typedef typename _Base::_Tp_alloc_type    _Tp_alloc_type;
  // ...

public:
  typedef _Tp value_type;
  typedef __gnu_cxx::__normal_iterator<pointer, vector>  iterator;
  typedef __gnu_cxx::__normal_iterator<const_pointer, vector> const_iterator;
  // ...

public:
  iterator begin();
  const_iterator begin() const;
  // ...
;

如果方法是公开的,你也应该将 typedef 声明为公开的。保持连贯一致。


正如其他人所说,从C++11开始,最好使用using而不是typedef,见What is the difference between 'typedef' and 'using' in C++11?

【讨论】:

以上是关于头文件和源文件中的 typedef 返回类型 - 最佳实践是啥?的主要内容,如果未能解决你的问题,请参考以下文章

typedef 的头文件最佳实践

C - #include 和多个 typedef

无法重新定义枚举 typedef

结构的类型定义似乎没有在头文件中通过?

sizeof和strlen

sizeof和strlen