Search code examples
c++c++14aliastypedeffinal

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


Is it possible to mark an alias of a type as final (i.e. can't be re-defined in derived class)?

#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.
}

According to http://en.cppreference.com/w/cpp/language/final, it is for function only.
Is there a workaround?

It would be useful as a coder fool-proof in some cases.


Solution

  • No, you cannot.

    Trait based types could do it, but the machinery is ugly.

    Define a distributed type map maybe via an adl based tag function map.

    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 {};}
    };
    

    now overriding A's type_tag works with trait::type<> but if you try the same with B you'll get a long incomprehensible error.

    This is a bad plan.

    Metaclasses will probably let you do something like this as well.

    In general, both of these require writing a new sublanguage of C++ to enforce a constraint C++ does not enforce. Possible, but ill-advised unless you have an extemely good reason.