如何自我记录在另一个特定库中使用的类型别名?
在下面的示例中,类User
定义了一个别名User::type
,该别名应该仅通过Library
在类T::type
中引用。
这是图表: -
Library<T>
仅预期T
定义某个别名(例如,此示例中为T::type
)。
#include <iostream>
class Base{}; //dummy for the sake of example
template<class T>class Library{
Base* t=nullptr;
public: typename T::type getValue(){return static_cast<typename T::type>(t);}
//some complex function, e.g. T::aType::doSomething()
};
在实际情况中,Library<T>
预期多个别名,例如T::aType
,T::bType
,T::callbackType
等
要使用上述库,必须定义::type
,例如如下: -
class Derived : public Base{}; //dummy for the sake of example
class User{
public: using type=Derived*;//<-- poorly documented
//... other alias e.g. aType=int*, bType=SomeClass*
//... other complex functions
};
以下是用法( full demo ): -
int main(){
Library<User> lib;
lib.getValue();
std::cout<<"OK"<<std::endl;
}
请注意User::type
确实缺乏自我记录
在现实生活中,大多数程序员 - 包括设计它的人 - 忘记了User::type
的用途。
User::type
未在User.h
内部引用,因此一些编码人员可以随意删除它。
我觉得我们心爱的代码是从内部腐烂的,我想到了保存它的方法。
如何自我记录类型别名以指示名称的方式/位置?
class User{
/** It is used for Library.h */
public: using type=Derived*;
它变得非常快,我仍然更喜欢使用C ++ - 语义而不是随机评论。
class User{
/** It is used for Library.h */
public: using LIBRARY_type=Derived*;
非常混乱。
注意:此问题与How to self-document a callback function that is called by template library class?类似,但此问题约为type-def
,而该问题约为callback
。
答案 0 :(得分:2)
这里你唯一真正的问题似乎是“某些程序员随机删除它是一个容易攻击的目标”。
解决这个问题的方法不是要考虑自我文档和名称,而是要进行同行评审和回归测试。为什么团队中的程序员“随意删除”并侥幸逃脱?这需要停止。
您可以通过简单的代码注释来保存必须回滚此类更改的管理难题:
/**
* Provided for use by Library.
*/
using type=Derived*;
就是这样。这就是你所需要的。它丝毫不是“脏” - 它告诉其他编码器为什么存在type
声明,如果有人删除它,它会像diff
中的一个痛苦的拇指一样突出。然后你可以问他们,“你是如何得出结论,图书馆不再需要这个声明,为什么要删除它值得破坏我们的API?”
简而言之,这只是一个人为问题。标准的成员类型库中有很多名为type
的例子,所以从技术角度来看,你已经在做你应该做的事了。不要试图反映您的类型的预期用途的名称;使名称描述是的内容。 C ++委员会用std::move
犯了同样的错误!
答案 1 :(得分:1)
您可以为此创建一个类型特征:
template <typename T> struct library_trait; // No definition
// Need to define library_trait<T>::type for ...
// library_trait<T>::atype for ...
在课程Library
中,使用library_trait<T>::type
代替typename T::Type
在使用Library<User>
之前的位置(如示例中的main
所示):
library_trait
专门User
。
template <> struct library_trait<User>
{
using type = Derived*;
// ...
};
答案 2 :(得分:1)
特质类是此问题的常用解决方案。它很尴尬,因为你必须在它自己的命名空间内专门化它。
另一种方法是创建一个特质函数。
namespace utility {
template<class T>struct tag_t{using type=T; constexpr tag_t(){}};
template<class T>constexpr tag_t<T> tag{};
template<class Tag> using type_t=typename Tag::type;
}
namespace MyLibrary {
template<class T>
void library_trait_name_f(tag_t<T>,...);
template<class T>using library_trait_name=type_t<decltype(library_trait_name_f(tag<T>))>;
}
现在支持你:
namespace Elsewhere {
struct Foo;
}
你可以在任何地方写:
namespace Elsewhere { // or MyLibrary, or even utility
tag_t<int> library_trait_name_f(tag_t<Foo>);
}
如果可见,它将被MyLibrary::library_trait_name<T>
选中。特别是,MyLibrary::library_trait_name<Elsewhere::Foo>
为int
。
这样做的好处是它允许您在库和库中的类型之间,类型旁边或第三个位置编写thr绑定。缺点是缺乏范围和非常规性。
MSVC对基于decltype的SFINAE也表现不佳,因此在MSVC中使用上述SFINAE时需要小心。