5

I have a group of classes and inside each one is defined a nested class always with the same name.

class A {
public:
    class Common {};
    ...
};

class B {
public:
    class Common {};
    ...
};

I am trying to write a template function like this (NOT compiling code):

template<typename T, typename... ARGS>
void foo(T::Common tc, ARGS... args) {
    T t(tc, args...);
    // do stuff
}

And I would like to use it as follow:

{
    ...
    A::Common ac();
    foo(ac, 42, "Hello");
    ....
}

How could I extract the outer class type from the type on the inner class instance passed to the function? Maybe with <traits>, but I am very newbie about it...

JFMR
  • 23,265
  • 4
  • 52
  • 76
Marco Stramezzi
  • 2,143
  • 4
  • 17
  • 37

2 Answers2

7

In your following function template:

template<typename T, typename... ARGS>
void foo(T::Common tc, ARGS... args);

T is in a non-deducible context. Therefore, doing:

foo(ac, 42, "Hello");

won't compile, because T can't be deduced from the function call arguments. You would need to explicitly pass A as an argument to the T template parameter:

foo<A>(ac, 42, "Hello");

However, note that T::Common will actually have to be preceded by the keyword typename, since Common is a type-dependent name:

template<typename T, typename... ARGS>
void foo(typename T::Common tc, ARGS... args);

Extracting the outer class type without giving up implicit type deduction

You could declare an outer_class_of<> class template for extracting the outer class. This template would be parameterized by the inner class type:

// primary template
template<typename>
struct outer_class_of;

Then, specialize this template for both A::Common and B:Common:

// specialization for A::Common
template<>
struct outer_class_of<A::Common> {
    using type = A; // outer class of A::Common
};

// specialization for B::Common
template<>
struct outer_class_of<B::Common> {
    using type = B; // outer class of B::Common
};

You can declare then an alias template for achieving C++14-like _t type traits:

template<typename T>
using outer_class_of_t = typename outer_class_of<T>::type;

This way, outer_class_of_t<A::Common> corresponds to A and outer_class_of_t<B::Common> to B.

Finally, you need to change your foo() function template definition to:

template<typename TCommon, typename... ARGS>
void foo(TCommon tc, ARGS... args) { 
   outer_class_of_t<TCommon> t(tc, args...);
}

When calling foo() with a A::Common or a B::Common object as function argument, TCommon will be deduced to A::Common or B::Common, respectively. outer_class_of<> is then applied on TCommon to obtain the type of the outer class.


Also, be ware of C++'s most vexing parse in:

A::Common ac();

What you want is actually:

A::Common ac{};

Otherwise, in the call to foo(), TCommon will be deduced to A::Common(*)() (i.e.: a pointer to function) instead of A::Common, since the former is declaring a function that takes no parameters and returns an A::Common object, whereas the latter is actually declaring an A::Common object.

JFMR
  • 23,265
  • 4
  • 52
  • 76
0

The answer above is good, but is somewhat complex.

Perhaps a simpler answer (that you are looking for) is that you can do it by explicit template spec calling rather than implicit deduction. This is a question of where you trade off the complexity.

Perhaps it is acceptable for your caller to specify A in the call and write:

foo(ac, 42, "Hello");

You should still put the typename keyword in and a good comment to help the user of your interface

template<typename T, typename... ARGS>
void foo(typename T::Common tc, ARGS... args); /// Call with foo<T>(tc, ...)
Gem Taylor
  • 5,381
  • 1
  • 9
  • 27