【问题标题】:Member enum class of template type as function argument模板类型的成员枚举类作为函数参数
【发布时间】:2019-12-12 20:45:27
【问题描述】:

我想创建一个模板函数作为参数,而不是模板类型本身,而是模板类型的成员枚举类。这可能吗?

以下代码演示了我想要实现的目标,我想知道是否有一些模板魔术可以为我完成它。

#include <iostream>

using namespace std;

class A{
public:
    enum class ID {a1, a2};

    static void f(ID id){
        switch(id){
        case ID::a1:
            std::cout << "a1\n";
            break;
        case ID::a2:
            std::cout << "a2\n";
            break;
        }
    }
};

class B{
public:
    enum class ID {b1};

    static void f(ID id){
        switch(id){
        case ID::b1:
            std::cout << "b1\n";
            break;
        }
    }
};

template<typename TypeName>
void g(TypeName::ID id){
    TypeName::f(id);
}

int main(int argc, char **argv){
    g(A::ID::a1);
    g(A::ID::a2);
    g(B::ID::b1);

    return 0;
}

想要的输出是

a1
a2
b1

特别要注意我希望函数 g 如何将 TypeName::ID 作为参数而不是 TypeName。

首选 C++11 解决方案,但适用于更高版本的解决方案也值得关注。

【问题讨论】:

  • 'g' 中的 TypeName 模板参数在不可演绎的上下文中,因此调用者必须明确地专门化 'g'。但请注意,您可以将 ID 类型本身作为模板参数 - 为什么它是内部类型很重要?
  • @IgorR。在我上面给出的例子中,g 需要在 TypeName 上调用一个静态函数。在我使用它的真实示例中,我对应的 g 需要调用另一个模板函数,其中 TypeName 作为模板参数传递。

标签: c++ c++11


【解决方案1】:
#include <iostream>

using namespace std;

class A{
public:
    enum class ID {a1, a2};

    static void f(ID id){
        switch(id){
        case ID::a1:
            std::cout << "a1\n";
            break;
        case ID::a2:
            std::cout << "a2\n";
            break;
        }
    }
};

class B{
public:
    enum class ID {b1};

    static void f(ID id){
        switch(id){
        case ID::b1:
            std::cout << "b1\n";
            break;
        }
    }
};

template<typename TypeName>
void g(typename TypeName::ID id){
    TypeName::f(id);
}

int main(int argc, char **argv){
    g<A>(A::ID::a1);
    g<A>(A::ID::a2);
    g<B>(B::ID::b1);

    return 0;
}

【讨论】:

【解决方案2】:
template<typename TypeName>
void g(TypeName::ID id){ // WRONG
    TypeName::f(id);
}

需要typename:

template<typename TypeName>
void g(typename TypeName::ID id){
    TypeName::f(id);
}

但是,类型是不可推断的(因为::)。 所以在调用站点,你必须指定类型:

g<A>(A::ID::a1);
g<A>(A::ID::a2);
g<B>(B::ID::b1);

作为替代方案,为了保持您预期的调用站点语法,您可以创建 trait 以将(枚举)类型绑定到类:

template <typename E> struct ClassFromId;

template <> struct ClassFromId<A::ID> { using type = A; };
template <> struct ClassFromId<B::ID> { using type = B; };


template<typename E>
void g(E id){
    ClassFromId<E>::type::f(id);
}

Demo

【讨论】:

  • 感谢您展示替代方案!在调用中避免额外的模板参数对我来说并不重要,但知道它是可能的非常有用。我有一个问题。我可以依靠最后一个替代方案来成为纯粹的完全编译时解决方案吗?对于这些特定的类,我对即使是很小的性能损失也非常敏感。在我看来,“使用 type = A”是一个永远不会被实例化的结构的编译时属性,并且不会从此代码生成运行时指令。但只是想确保我没有遗漏任何东西。
  • 从枚举推断类类型的机制是编译时间:没有运行时惩罚。
猜你喜欢
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多