【问题标题】:Is it possible to auto-deduce the base class of a function template's template argument?是否可以自动推断函数模板的模板参数的基类?
【发布时间】:2016-03-16 04:31:47
【问题描述】:

是否可以编写一个模板函数来拥有关于模板参数的基类的类型信息? (假设模板参数只派生自一个类)

所以,我正在寻找这样的东西:

template <class T> 
auto f(T t) -> decltype(...) { // ... is some SFINAE magic that 
                               //     catches B, the base of T
    std::cout << (B)t << std::endl;
}  

编辑:一些相关背景。我正在编写A* 算法的通用实现。模板参数是一个Node 结构。因此,用户可以定义:

struct NodeBase {
    REFLECTABLE((double)g, (double)f)
        // Using the REFLECTABLE macro as described here:                    
        // http://stackoverflow.com/a/11744832/2725810 
};

struct NodeData : public NodeBase {
    using Base1 = NodeBase;
    REFLECTABLE((double)F)
};

我想写一个打印节点结构内容的函数。 REFLECTABLE 完成了提取struct 字段的所有艰苦工作。但是,当用户给我一个NodeData 实例时,我的函数还需要打印NodeBase 组件的内容。我想稍后为两个和三个基类添加我的函数的重载。

【问题讨论】:

  • 你有潜在基类的列表吗?
  • 此代码将成为库的一部分,而 T 来自客户端。所以,没有。
  • 您可以添加专门的 type_traits 和 template &lt;&gt; struct base_type&lt;Derived1&gt; { using type = Base1;}; 或要求 CRTP 直接在类中添加类型信息。
  • 如果你没有得到基类的列表,如果你有class B : private C, D {};,你怎么知道是要选择C还是D?跨度>
  • 请同时添加你想用它做什么,因为似乎自动基类检测可能不是正确的做法,或者你对基类的知识有什么用?

标签: c++ c++11


【解决方案1】:

您正在寻找的 SFINAE 东西通常是不可能的。你会为多重继承做什么?私有/受保护的继承?而且我敢打赌,我还没有考虑过更复杂的问题。我看到的唯一方法是始终使用一些反射库(而不仅仅是一个宏),您可以在其中明确定义您想要获得的结果。

相反,您可能正在寻找的非 SFINAE 和非令人兴奋的方式是简单地为 NodeBase 重载您的函数一次,然后通过 (const-)reference 传递派生类型:

auto f(NodeBase const& t)
{
    std::cout << t << std::endl;
} 

调用f(NodeData{}) 将自动获得对基类NodeBase 的引用。请注意,此处的引用很重要,否则您将分割派生对象。

【讨论】:

    【解决方案2】:

    如果您知道您的类可以继承哪些类型,这里有一种方法可以为每个实例挖掘出所有实际的基类。
    它遵循一个最小的工作示例:

    #include<type_traits>
    #include<iostream>
    
    template<int i>
    struct check: check<i+1> { };
    
    template<>
    struct check<3> { };
    
    struct B1 { };
    struct B2 { };
    struct B3 { };
    
    struct D: B1, B3 { };
    
    template<class T>
    typename std::enable_if<std::is_convertible<T, B1>::value>::type
    f(check<0>, T t) {
        B1 b1 = static_cast<B1>(t);
        std::cout << "B1" << std::endl;
        f(check<1>{}, t);
    }
    
    template<class T>
    typename std::enable_if<std::is_convertible<T, B2>::value>::type
    f(check<1>, T t) {
        B2 b2 = static_cast<B2>(t);
        std::cout << "B2" << std::endl;
        f(check<2>{}, t);
    }
    
    template<class T>
    typename std::enable_if<std::is_convertible<T, B3>::value>::type
    f(check<2>, T t) {
        B3 b3 = static_cast<B3>(t);
        std::cout << "B3" << std::endl;
        f(check<3>{}, t);
    }
    
    template<class T>
    void f(check<3>, T) {
        std::cout << "end" << std::endl;
    }
    
    template <class T> 
    void f(T t) {
        f(check<0>{}, t);
    }
    
    int main() {
        D d;
        f(d);
    }
    

    注意它如何跳过涉及B2 的函数,因为它不是D 的基本类型。
    如果您要继承的类型超过 3 个,此方法也很容易扩展,它不会强制固定继承链,并且您可以对不同的类型执行不同的操作。

    【讨论】:

    • 我认为您的意思是 std::is_base_of 而不是 std::is_convertible。 std::is_convertible&lt;T, U&gt;::value 将是 true,例如,如果 T 有一个重载的转换运算符到 U。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-18
    • 2018-04-25
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    相关资源
    最近更新 更多