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

Mar*_*zzi 5 c++ templates traits c++11 c++14

我有一组类,在每个类中都定义了一个始终具有相同名称的嵌套类。

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

class B {
public:
    class Common {};
    ...
};
Run Code Online (Sandbox Code Playgroud)

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

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

我想按如下方式使用它:

{
    ...
    A::Common ac();
    foo(ac, 42, "Hello");
    ....
}
Run Code Online (Sandbox Code Playgroud)

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

眠りネ*_*ネロク 5

在您的以下功能模板中:

template<typename T, typename... ARGS>
void foo(T::Common tc, ARGS... args);
Run Code Online (Sandbox Code Playgroud)

T是在不可推论的上下文中。因此,做:

foo(ac, 42, "Hello");
Run Code Online (Sandbox Code Playgroud)

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

foo<A>(ac, 42, "Hello");
Run Code Online (Sandbox Code Playgroud)

但是,请注意,T::Common实际上必须以关键字 开头typename,因为它Common是依赖于类型的名称:

template<typename T, typename... ARGS>
void foo(typename T::Common tc, ARGS... args);
Run Code Online (Sandbox Code Playgroud)

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

您可以声明一个outer_class_of<>用于提取外部类的类模板。此模板将由内部类类型参数化:

// primary template
template<typename>
struct outer_class_of;
Run Code Online (Sandbox Code Playgroud)

然后,将此模板专门用于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
};
Run Code Online (Sandbox Code Playgroud)

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

template<typename T>
using outer_class_of_t = typename outer_class_of<T>::type;
Run Code Online (Sandbox Code Playgroud)

通过这种方式,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...);
}
Run Code Online (Sandbox Code Playgroud)

foo()使用 aA::Common或一个B::Common对象作为函数参数调用时,TCommon将分别推导为A::CommonB::Commonouter_class_of<>然后应用TCommon到获取外部类的类型。


另外,请注意C++ 中最令人头疼的解析

A::Common ac();
Run Code Online (Sandbox Code Playgroud)

你想要的其实是:

A::Common ac{};
Run Code Online (Sandbox Code Playgroud)

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