从传递给模板函数的内部 class 实例中提取外部 class 类型

Extract outer class type from inner class instance passed to template function

我有一组 classes 并且在每个里面都定义了一个嵌套的 class 总是具有相同的名称。

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

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

我正在尝试编写这样的模板函数(不编译代码):

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

我想按如下方式使用它:

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

如何从传递给函数的内部 class 实例的类型中提取外部 class 类型?也许 <traits>,但我是新手...

在您的以下函数模板中:

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

T 处于 non-deducible 上下文中。因此,做:

foo(ac, 42, "Hello");

无法编译,因为 T 无法从函数调用参数中推导出来。您需要将 A 作为参数显式传递给 T 模板参数:

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

但是,请注意 T::Common 实际上必须以关键字 typename 开头,因为 Common 是一个 type-dependent 名称:

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

在不放弃隐式类型推导的情况下提取外部 class 类型

您可以声明一个 outer_class_of<> class 模板来提取外部 class。该模板将由内部 class 类型参数化:

// primary template
template<typename>
struct outer_class_of;

然后,将此模板专门用于 A::CommonB: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
};

然后您可以声明一个别名模板以实现类 C++14 _t 类型特征:

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

这样,outer_class_of_t<A::Common>对应Aouter_class_of_t<B::Common>对应B

最后,您需要将 foo() 函数模板定义更改为:

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

当使用 A::CommonB::Common 对象作为函数参数调用 foo() 时,TCommon 将推导为 A::CommonB::Common, 分别。 outer_class_of<> 然后应用于 TCommon 以获得外部 class.

的类型

此外,请注意 C++'s most vexing parse 中的:

A::Common ac();

你要的其实是:

A::Common ac{};

否则,在对 foo() 的调用中,TCommon 将被推导为 A::Common(*)()(即:指向函数的指针)而不是 A::Common,因为前者正在声明一个不带参数的函数和 returns 一个 A::Common 对象,而后者实际上是在声明一个 A::Common 对象。

上面的答案很好,但有点复杂。

也许(您正在寻找的)更简单的答案是您可以通过显式模板规范调用而不是隐式推导来完​​成。这是一个在何处权衡复杂性的问题。

也许您的呼叫者可以在呼叫中指定 A 并写入:

foo(ac, 42, "Hello");

您仍然应该放入 typename 关键字和一个好的评论来帮助您的界面用户

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