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

时间:2018-02-22 08:03:01

标签: c++ c++11 templates c++14 traits

我有一组类,每个类都定义了一个总是具有相同名称的嵌套类。

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");
    ....
}

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

2 个答案:

答案 0 :(得分:3)

在以下功能模板中:

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

T处于不可扣除的背景中。因此,做:

foo(ac, 42, "Hello");

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

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

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

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

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

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

// 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...);
}

使用foo()A::Common对象作为函数参数调用B::Common时,TCommon将推断为A::CommonB::Common,分别。然后在outer_class_of<>上应用TCommon以获取外部类的类型。

此外,请在C++'s most vexing parse中使用

A::Common ac();

你想要的实际上是:

A::Common ac{};

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

答案 1 :(得分:0)

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

或许更简单的答案(您正在寻找)是您可以通过显式模板规范调用而不是隐式演绎来实现。这是一个你在哪里权衡复杂性的问题。

调用者可能会在调用中指定A并写入:

foo(ac,42,&#34; Hello&#34;);

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

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