【问题标题】:C++17: map type to integer value at compile timeC++17:在编译时将类型映射到整数值
【发布时间】:2019-12-06 15:08:44
【问题描述】:

我有以下几种:

struct A { }; 
struct B { }; 
struct C { }; 

template <typename Class, uint16_t i>     
struct def {                              
        using message_type = Class;       
        static constexpr uint16_t tag = i;
};                                        

还有这个元组:

constexpr auto types = std::make_tuple(def<A, 1>(), def<B, 2>(), def<C, 3>());

类型 A、B 和 C 应映射到相应的值(A -> 1 等)。我想创建一些东西(函数,结构),这些类型之一的给定对象将返回正确的值。我尝试执行以下操作:

template <typename T>                                                                    
struct gettag {                                                                          
        static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
};                                                                                       

template <typename... Args>                                                              
struct tagdb : public gettag<Args>... {                                                  
        tagdb(std::tuple<Args...> const& t) { }                                          
};                                                                                       
int main() {                            
        tagdb t(types);                 
        A a;                            
        std::cout << t.value(a) << '\n';
}                                       

这不起作用,g++ 声称对成员 value 的请求不明确:

x.cc: In function ‘int main()’:
x.cc:29:17: error: request for member ‘value’ is ambiguous
   29 |  std::cout << t.value(a) << '\n';
      |                 ^~~~~
x.cc:16:26: note: candidates are: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<C, 3>; decltype (T::tag) = const short unsigned int; typename T::message_type = C]’
   16 |  static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
      |                          ^~~~~
x.cc:16:26: note:                 ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<B, 2>; decltype (T::tag) = const short unsigned int; typename T::message_type = B]’
x.cc:16:26: note:                 ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<A, 1>; decltype (T::tag) = const short unsigned int; typename T::message_type = A]’

我有点惊讶,特别是因为它清楚地表明每个方法都使用不同的类型进行参数化。

有没有办法让这个解决方案发挥作用,或者我应该完全改变我的方法?请注意,我最想避免的是为每种类型编写重载。

【问题讨论】:

    标签: c++ templates c++17 template-meta-programming


    【解决方案1】:

    问题是你有模板基类,所有这些都声明了一个同名的成员。最简单的解决方法是将所有基类value 函数拉入派生类:

    using gettag&lt;Args&gt;::value...;

    https://godbolt.org/z/F_Prhg

    【讨论】:

    • 谢谢,这确实很有魅力。尽管模板基类声明了相同的名称,但它们使用不同的参数类型。那么哪条规则说没有“使用”就不能使用它们?我想这是基本的东西。
    • 它的名称查找:依赖于外部类模板的模板参数的类模板的成员模板基类不考虑查找非依赖名称。 using 使它们成为 tagdb 类的成员,因此在名称查找过程中会考虑它们。
    • 好的,这不是那个基本的,谢谢。难怪我不知道发生了什么。这似乎过于复杂,尽管我认为不这样做有时会使事情以非常神秘的方式工作。
    【解决方案2】:

    我建议一个没有std::tuplegettag 的解决方案:

    struct A { }; 
    struct B { }; 
    struct C { }; 
    
    template <typename Class, std::uint16_t i>     
    struct def {                              
        static constexpr std::uint16_t value(Class) {
            return i;
        }
    };  
    
    template <typename... Tags>                                                              
    struct tagdb : public Tags... {                                                  
        using Tags::value...;
    };
    
    template<class... Tags>
    constexpr auto make_tagdb(Tags...) {
        return tagdb<Tags...>{};
    }
    
    // template<class... Tags>
    // constexpr auto make_tagdb(std::tuple<Tags...>) {
    //     return tagdb<Tags...>{};
    // }
    
    constexpr auto tags = make_tagdb(def<A, 1>(), def<B, 2>(), def<C, 3>());
    
    int main() {                            
        A a;                            
        std::cout << tags.value(a) << '\n';   // Output: 1
    }  
    

    【讨论】:

    • 很好的解决方案,谢谢,不幸的是,这个元组是必须的,它是由 protobuf 编译器插件生成的,我无法更改它。除此之外,您的解决方案非常好,我会看看将来是否可以在我们的解决方案中使用它。谢谢。
    • @JędrzejDudkiewicz 您可以使用注释重载从std::tuple 构造tagdb。实际上,在我输入答案后,我意识到我通过删除看起来多余的 gettag 类,基本上重复了 Anthony 的答案。
    猜你喜欢
    • 2021-10-10
    • 2011-12-06
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-22
    相关资源
    最近更新 更多