3

Let's say I have a class Class, which has an associated enum member Enum. The enum only makes sense in the context of this class, and therefore it is specified inside it. Then you can just call Class::Enum::Something from outside of the class, which is fine.

class Class
{
   public:
      enum class Enum : uchar
      {
          Something
      }
}

However, if the class Class is templated, you cannot do Class::Enum::Something, but you have to Class<T>::Enum::Something (or Class<>::Enum::Something if T has some default type).

template <typename T = double>
class Class
{
   public:
      enum class Enum : uchar
      {
          Something
      }
}

The Enum has to be the same for all Ts, since it's just a simple enum, but the T always has to be specified anyway.

My question is - is there a clever way of avoiding this?

3
  • 5
    You could add your class declaration to a namespace, and then add the enum to the namespace rather than the class. Commented Mar 7, 2022 at 13:52
  • Minor point on terminology: you cannot "call" Class::Enum::Something. It's not a function. Commented Mar 7, 2022 at 13:57
  • The Enum is different in each template instantiation, despite having all the same names. If you want the same Enum you can't put it inside a template. One possibility would be to define it in a class and use that class as a base class for the template. Commented Mar 7, 2022 at 13:58

2 Answers 2

5

It is essential to understand that the wrapped enum type is a different type for different specializations of Class.

#include <type_traits>

template <typename T = double>
struct S
{
      enum class E : int { a };
};

static_assert(!std::is_same_v<S<>::E, S<int>::E>);  // !!!

It seems like you want this to actually be the same type, in which case you could break it out into a separate class and use private implementation inheritance to expose it via the class template specializations:

#include <type_traits>

namespace detail {
struct Base {
    enum class E : int { a };
};
}  // namespace detail;

template <typename T = double>
struct S : private detail::Base
{
    using Base::E;    
};

static_assert(std::is_same_v<S<>::E, S<int>::E>); // OK

However if you are set on supplying this enum as a member (alias declaration above) of the various class template specializations, you will need to qualify which specialization you refer to.

S<>::E;
S<int>::E;

The root problem you seem to want to solve is scoping, in which case you may use a namespace instead of class to wrap the associated enum:

namespace my_lib {

template <typename T = double>
struct S {};

enum class E : int { a };

}  // namespace my_lib
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the extensive answer. I am aware that the wrapped enums are different types, and exactly as you said, the issue I want to solve is scoping. I guess I will have to resort to the namespace solution, even though that forces me to de-couple the enum from the class and "expose" the enum to everything else inside the same namespace.
@Jixxy don't think java. Not everything that belongs to a class must be defined inside the class. If you accept that, then defining Enum outside of Class is no worse than having it nested inside
1

Often it is more convenient to move everything that does not depend on the tempalte argument to a non-template base class:

#include <iostream>

class Base {
   public:
      enum class Enum
      {
          Something
      };

};

template <typename T = double>
class Class : public Base
{
};

int main() {
    std::cout << (Class<>::Enum::Something == Class<int>::Enum::Something);
}

5 Comments

Yes, or Base::Enum::Something instead of Class<T>::Enum::Something. Too bad the standard doesn't allow Class::Enum::Something in this case when the enum is inside a non-templated base class, and thus cannot possibly depend on T.
@Jixxy it does depend on T. Also in my code. You can for example write a specialization Class<int> that does not inherit from Base, then there is no Class<int>::Enum. In some sense, everything in the template depends on T
@Jixxy -- a specialization of Class doesn't have to have that base class, so the presence of Base::Enum::Something does, in theory, depend on T. template <> class Class<int> {}; is a valid specialization.
@PeteBecker Oh ok I've never seen that, thanks
@Jixxy Note a subtle different between this approach and the very similar one of mine: as you use public inheritance here, it is publically visible that "Class is a Base". As we use implementation inheritance here, we typically don't want that, particularly as the inheritance offers no polymorphic behaviour for "Class is a Base". That we are using Base for implementation inheritance is an implementation technique, and something we should hide from clients. Imo this answer offers a worse alternative to the technique that was already presented in my own answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.