5

Is there a way to implicitly reference a class' name at compile-time?

Specifically, if I want to declare an instance of template class A using class B from within the scope of class B, is there a way to avoid explicit reference to "B" in the syntax to declare the class A instance?

To better-illustrate with an example:

// main.cpp

#include <iostream>

using namespace std;

template <typename T>
class A
{
public:
  typedef void (T::*TFunc)();

  A( T& t ) : t_( t ) {}

  void callFunc( TFunc tFunc ) { (t_.*tFunc)(); }

private:
  T& t_;
};

class LongClassName
{
public:
  LongClassName() : a_( *this ) {}

  void pubFunc()
  {
    a_.callFunc( &LongClassName::privFunc ); // Can I avoid explicitly using "LongClassName" here?
  }

private:
  void privFunc() { cout << __PRETTY_FUNCTION__ << endl; }

  A<LongClassName> a_; // Can I avoid explicitly using "LongClassName" here?
};

int main( int argc, char* argv[] )
{
  LongClassName().pubFunc();
  return 0;
}

What I've tried:
Read Is there a __CLASS__ macro in C++?, so I'm aware there's no __CLASS__ (pseudo-equivalent to __FUNCTION__) preprocessor macro. Some of the solutions to that post extract the class name from __PRETTY_FUNCTION__ - but that is a runtime solution not applicable to this situation.

I've read conflicting information on StackOverflow about whether typeid(T) is run time or compile time; either way, A<typeid(*this).name()> a_; doesn't compile, and looks wrong anyhow: there's obviously no this in that context.
By my reading, the text at https://en.cppreference.com/w/cpp/language/typeid makes clear that typeid is runtime, and so not-applicable to this situation.

StoneThrow
  • 3,821
  • 11
  • 42

2 Answers2

8

There is no way to avoid using the type name in LongClassName::privFunc and A<LongClassName> a_;.

That said, you can still make your life easier. You can make an alias for LongClassName that you can use it it's place. Adding

using LCN = LongClassName;

will let you use LCN in place of LongClassName

NathanOliver
  • 150,499
  • 26
  • 240
  • 331
4

You can declare local alias template with redefined default parameter to avoid using class name in second case:

template<typename T = LongClassName> using
A = ::A<T>;
A<> a_; // Can I avoid explicitly using "LongClassName" here?

As for shorter name for LongClassName, there is a common convention to declare corresponding type alias with a common name. It will also be helpful for writing copy / move constructors, etc:

class LongClassName
{
public:
  using Self = LongClassName;

  LongClassName() : a_( *this ) {}

  LongClassName(Self const &); // copy constructor

  Self & operator =(Self const &); // copy assignment operator

  void pubFunc()
  {
    a_.callFunc( &Self::privFunc ); // Can I avoid explicitly using "LongClassName" here?
  }

private:
  void privFunc() { cout << __PRETTY_FUNCTION__ << endl; }

  A<Self > a_; // Can I avoid explicitly using "LongClassName" here?
};
user7860670
  • 32,142
  • 4
  • 44
  • 75
  • 1
    That's a clever way to approach this, and seems like a good convention - only one explicit reference to the class name, and has easy readability everywhere else. – StoneThrow Nov 07 '19 at 20:33
  • Would you use `Self` if the class name were short (for consistency with cases when it is long)? In other words, is it a good practice to use `Self` instead of a class name in general? – Evg Nov 07 '19 at 21:03
  • 1
    @Evg Yes, I typically always declare this alias. Note that it is useable even if class name is not that long. For example it makes it possible to quickly locate copy / move constructors or member function pointers using normal search and simplifies refactoring. I think that there was even a proposal to add into language some sort of built-in identifier (in addition to `this`) for this purpose. – user7860670 Nov 07 '19 at 21:09
  • Will use it, too. Thanks! Just another question: are there any use cases for the public member type? Should it be `public` or `private`? – Evg Nov 07 '19 at 21:28
  • This isn't what I'd call common practice in C++ programs. I've used it, with the same `Self` naming convention. I also use `using Super = MyBaseClass;` (Object Pascal influence). – Eljay Nov 08 '19 at 12:33