【问题标题】:From enum type declared in a class/struct get to a peer static method also declared in that class/struct?从类/结构中声明的枚举类型到在该类/结构中也声明的对等静态方法?
【发布时间】:2021-01-15 06:08:15
【问题描述】:

简而言之:在一个以枚举类型作为参数的模板中(不是一个作用域枚举,一个常规的旧的 C++11 之前的枚举)我需要调用一个方法,声明在同一个struct 作为枚举,它以一个枚举值作为其参数。有没有办法做到这一点?

完整:假设我是“自我范围”的无范围枚举,如下所示:

struct W {
   enum E { A, B, C };

   static string foo(enum E e);
};

现在假设我有一堆遵循这种模式的结构声明 - 每个都有一个枚举 虽然枚举类型的 name 不同并且每个都有一个静态方法 @987654325 @ 采用该枚举类型的一个参数。

现在我想创建一个模板,给定一组这些枚举类型,希望根据自己的foo() 转换每个枚举类型:

template <typename E>
vector<string> xform(const vector<E> es) {
  vector<string> ss;
  for (E e : es) {
    ss.push_back(foo(e));
  }
  return ss;
}

现在我实例化xform:

...
    vector<enum W::A> as{W::A, W::C};
    auto Wx = xform(as);
...

当然我得到一个编译器错误,因为编译器找不到正确的 foo 来调用:

prog.cc: In instantiation of 'std::vector<std::__cxx11::basic_string<char> > xform(std::vector<E>) [with E = W::A]':
prog.cc:34:24:   required from here
prog.cc:24:21: error: 'foo' was not declared in this scope
   24 |     ss.push_back(foo(e));
      |                  ~~~^~~

(这都是here on wandbox。)

所以我需要从枚举类型到它的对等方法。我可以这样做 - 怎么做? (由于对等方法的名称始终相同,所以这部分很简单——我不知道如何从枚举类型获取到它的封闭类型。)

(显然,如果每个结构中的枚举类型的 name 相同,我可以通过使用 struct's name 作为模板参数来解决这个问题。但在我的用例中枚举都有不同的名称。)

解决方案(此时)D-RAJ's answer 虽然非常简单,但在这种特殊情况下不起作用,因为函数 foo 依赖于模板函数xform中的类型参数,因此与查找和ADL相关的规则以及模板中的非依赖名称意味着如果您尝试隐式实例化xform,则不能使用ADL在其他命名空间的代码中。 (我不确定我理解为什么,但事实就是如此。)错误是在模板实例化点无法找到名称,只能在声明点(并且只能通过 ADL)找到。我想你可以通过显式实例化来解决这个问题......但是......

dxiv's answer 使用 trait 效果很好,并不繁琐,而且完全无需修改现有的封装枚举即可完成。

【问题讨论】:

    标签: c++ enums


    【解决方案1】:

    简单地说,不,你不能那样做。这有两个原因,

    1. vector&lt;enum W::A&gt;template &lt;typename E&gt; vector&lt;string&gt; xform(const vector&lt;E&gt; es) 不会让您找到哪个结构包含函数 foo()。这是因为std::vector::_Ty = enum W::A 基本上是enum 类型。如何从 enum 类型中找到 struct
    2. 在结构或类中定义静态foo() 函数时,该函数在结构/类的范围内(与namespace W { string foo(...) { ... } } 相同)。这是此错误的原因:prog.cc:24:21: error: 'foo' was not declared in this scope

    如何解决这个问题?
    我想到的唯一合理选项是将foo()函数放在全局范围内并为其提供覆盖。它看起来像这样。

    struct W1 {
        enum A { A, B, C };
    };
    
    string foo(enum W1::A a) { return std::array{ "A","B","C" } [a] ; }
    
    struct W2 {
        enum B { X, Y, Z, };
    };
    
    string foo(enum W2::B b) { return std::array{ "X", "Y", "Z" } [b] ; }
    

    现在xform() 函数可以解析在编译时使用哪个foo(),而不会出现任何范围问题。

    【讨论】:

    • 我建议像您一样使用免费功能,并在 foo 中使用本地 static std::array
    • 这是一个很好的建议——特别是如果声明为朋友(我认为)——如果所有这些包装的枚举类型都在同一个命名空间中,它们也不必是真正的全局...
    • 顺便说一句,整个问题是你的观点#1:你如何从枚举类型中找到一个结构?你提供了一个很好的解决方法,这让我很满意,但是......从技术上讲,没有回答你如何在 C++ 中做到这一点,或者提供一些与语言相关的论点为什么你不能!我想不出一种方法,但考虑到所有几乎神奇的模板元编程,可能会有一个方法,尤其是标准中看似数百种类型特征......
    • @davidbak 至于我的知识,如果我错了请纠正我,我认为没有办法。将enum W1::A 传递给std::vector 后,类型为enum W1::A。这意味着xform() 中的E 将是enum 类型。没有一种方法(在 type_traits 中)我们可以在没有解决方法的情况下完成。 Reference
    【解决方案2】:

    我不知道这是否可能,也不认为是。

    一种相对便宜的替代方法是手动维护名称之间的关联。在下面的示例代码中,这是通过帮助模板 WX&lt;&gt; 的特化来完成的(尝试过 here)。

    #include <string>
    #include <vector>
    using std::string;
    using std::vector;
    
    struct W1 {
      enum E1 { A, B, C };
      static string foo(enum E1 e);
    };
    
    struct W2 {
      enum E2 { A, B, C };
      static string foo(enum E2 e);
    };
    
    template<typename T> struct WX;
    template<> struct WX<W1::E1> { using W = W1; };
    template<> struct WX<W2::E2> { using W = W2; };
    
    template<typename E> vector<string> xform(const vector<E> es) {
      vector<string> ss;
      for (E e : es) {
        ss.push_back(WX<E>::W::foo(e));
      }
      return ss;
    }
    
    void bar()
    {
      vector<enum W1::E1> a1s { W1::A, W1::C };
      auto w1x = xform(a1s);
      vector<enum W2::E2> a2s { W2::A, W2::C };
      auto w2x = xform(a2s);
    }
    

    【讨论】:

    • @Klaus 上面的代码中没有“运行时容器”(当然,除了来自 OP 示例的原始向量)。与重载不同,这种方法不需要更改 OP 的现有类。
    • @Klaus 我的代码中唯一的std::vector 直接来自OP,无论如何与技术本身无关。请再次阅读原帖,包括 OP 的代码 sn-ps。我的回答是让 OP 的代码工作,只需最少的管道,无需重新设计。
    • 我不同意 :-) OP 有一个问题要解决。 OP 提供了一个想法,也远非最优。因此,在没有内存和运行时开销的情况下提供更好的解决方案感觉很好,因为它不是必需的。并在没有任何实际用例的情况下处理所有模板内容?为什么?我们经常看到没有经验的用户使用模板而不是简单的重载。因此,如果它适合问题并降低复杂性,最好删除不需要的模板。
    • @Klaus 在这种情况下,模板只会自动定义您建议手动执行的那些重载。我建议您根据重载发布自己的答案并进行比较。只需确保它编译了 OP 的特定 xform,这样就可以了。
    • @Klaus 你找错树了。这个WX&lt;&gt; 帮助器模板在编译代码中的占用空间完全为零,并且源代码中每个类的开销为一行。也就是说,如果您不喜欢模板,那很好,如果您有更好的解决方案,同样可以,如果您发布它就更好了。再见。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    相关资源
    最近更新 更多