使"typedef"成为最终的(或模拟它)

Make a "typedef" be final (or simulate it)

本文关键字:模拟 typedef      更新时间:2023-10-16

是否可以将type的别名标记为final(即不能在派生类中重新定义)?

#include <iostream>
class B{
public: using type=std::string;
};
class D : public B{
public: using type=int;     //<--- [1] I want a compile error here.
};
int main(){
typename D::type str="abc"; //<--- [2] This line is actually correct.
}

根据http://en.cppreference.com/w/cpp/language/final,仅用于功能
有变通办法吗?

在某些情况下,它将作为一个程序员的傻瓜证明是有用的。

不,不能。

基于特质的类型可以做到这一点,但机器很难看。

可以通过基于adl的标记函数映射来定义分布式类型映射。

template<class T>struct tag_t{constexpr tag_t(){} using type=T;};
template<class T>constexpr tag_t<T> tag{};
namespace trait {
template<class T>
constexpr void type_tag( tag_t<T> ){}
template<class T>
using type=typename decltype( type_tag( tag<T> ) )::type;
}
// non-final alias
struct A{
friend constexpr tag_t<int> type_tag(tag_t<A>){return {};}
};
// non-final alias
struct A{
friend constexpr tag_t<char> type_tag(tag_t<A>){return {};}
};
// final alias
struct B{
template<class T, std::enable_if_t< std::is_base_of<B,T>{}, bool> =true>
friend constexpr tag_t<std::string> type_tag(tag_t<T>){return {};}
};

现在,重写Atype_tag可以与trait::type<>一起使用,但如果您对B进行同样的尝试,您将得到一个长期无法理解的错误。

这是一个糟糕的计划。

元类可能也会让你做这样的事情。

一般来说,这两者都需要编写一个新的C++子语言来强制执行C++没有强制执行的约束。可能,但不明智,除非你有一个非常好的理由。

关于如何使用枚举或其他伪对象有效隐藏类型的切向答案。

/*
* Imagine everybody uses managed strings in our project throughout,
* so everyone expects to be able to declare and print strings everywhere,
* especially for debugging...
*/
typedef OurInternalMangedStrings string;
/***/
void InternalStringManager::ReallyDoingNastyInternalStuff()
{
// Within this code casually using managed strings 
// to format error messages, etc, 
// would be fatal as it will cause nasty recursion.
enum DoNotUseStrings_DeadlockDanger { string, OurInternalMangedStrings };
printError(string ("I had an error here and I don't know why - code ") << errCode);
}

这将产生一个错误,有望同时提到字符串和DoNotUseStrings_DeadlockDanger,从而提供线索。

但它对类型的用途有限,因为它虽然阻止了作者使用"字符串"一词,但并不能阻止代码自动执行转换,或使用已经存在的该类型的对象,例如,如果构造函数不明确,则以下内容将无注释通过:

printError("I had an error here and I don't know why at all!");

对于数据值,我发现它更有用:

void MyManager::Setup()
{
{    SomeFeature newPimple = new Somefeature;
enum DoNotUseMember {someFeature};
/** set up newPimple using other member data and parameters 
when it is ready I will assign it to the member variable "someFeature"
**/
/** any accidental use of a someFeature member will produce error message **/
// Ready to install the new pimpl as the visible feature
MUTEX_RAII(access_feature); // ... Or whatever might be needed
/* can still access someFeature by being explicit */
delete this->someFeature;
this->someFeature = newPimpl;
}
/** other setup code that uses the new feature **/
}

就我个人而言,我会将新实例称为someFeature,并免费获得隐藏行为,但许多人发现这个名称很难重复使用。

我使用这种技术的另一种方式是重构。我有一个方法,可以愉快地使用成员值来控制其行为,然后需要一个增强,其中一个控制值必须由外部控制。为了实现这一点,原始的无参数方法变成了一个填充程序,调用一个以成员为参数的新方法。

但是,如何确保新方法不会意外地使用成员而不是参数呢?就我个人而言,我会让争论掩盖成员,但我们再次受到他人理解的限制。