Can class name be referenced implicitly at compile-time?
Asked Answered
S

2

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.

Silures answered 7/11, 2019 at 19:56 Comment(4)
are you worried about too much typing or is there a real problem you are trying to solve?Yager
@formerlyknownas_463035818 - academic curiosity: it seems like it'd be "nice"/less code-hazardous to be able to refer to the class name implicitly.Silures
You could use decltype(*this) since that operates at compile time but as you said there is no this in that contextDisprize
decltype() is probably more like what you'd need, not typeid().Marvelofperu
H
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

Highboy answered 7/11, 2019 at 20:0 Comment(0)
D
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?
};
Devonne answered 7/11, 2019 at 20:21 Comment(5)
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.Silures
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?Surety
@Surety 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.Devonne
Will use it, too. Thanks! Just another question: are there any use cases for the public member type? Should it be public or private?Surety
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).White

© 2022 - 2024 — McMap. All rights reserved.