头文件和源文件中的 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 返回类型 - 最佳实践是啥?的主要内容,如果未能解决你的问题,请参考以下文章