【问题标题】:Static polymorphism with forwarding references具有转发引用的静态多态性
【发布时间】:2020-03-09 11:34:46
【问题描述】:

我正在尝试使用如下所示的简单示例中的静态多态性。

#include <iostream>

template <typename Derived>
struct Base
{
    decltype(auto) foo() { return static_cast<Derived&>(*this).foo(); }
};

struct Derived : Base<Derived>
{
    void foo() { std::cout << "Derived" << std::endl; }
};

template <typename T>
struct Traits;

template <typename T>
struct Traits<Base<T>>
{
    using Derived = T;
};


template <typename T>
struct Object
{
    template <typename U>
    Object(U&& data) : m_data(std::forward<U>(data)) {}

    T m_data;
};

template <typename T>
decltype(auto) polymorphicCall(T&& obj)
{
    using Derived = typename Traits<std::decay_t<T>>::Derived; 
    return Object<Derived>(static_cast<Derived&>(obj));
}

int main()
{
    Derived d;
    polymorphicCall(d);

    return 0;
}

问题是polymorphicCall 中的T 被推导出为Derived,这样任何东西都可以传递给该函数,甚至int。有没有办法只接受Base&lt;Derived&gt; 类型?

我尝试在模板参数上使用转发引用和enable_if,但无法推断出Bases 模板参数。

有没有办法同时使用转发引用和静态多态性?

编辑:更新了代码示例以包含实际的转发参考以及如何尝试使用它。

显示的错误是:“错误:无效使用不完整类型'struct Traits'”

链接:https://godbolt.org/z/3EcS47

【问题讨论】:

  • 我仍然觉得这很混乱。在polymorphicCall 中,您已经将T 推导出为Derived&amp;。然后你引入一个类型别名Derived,它会得到类型Derived?
  • 还要注意Object中的T不是转发引用,而是右值引用。
  • @super 我知道我有T 被推断为Derived,但这就是问题所在。这样,任何东西都可以被函数推断和接受。我只想接受Base&lt;Derived&gt; 类型
  • 如果这就是你想要实现的全部,你只需要改进你的类型特征并添加一个static_assert
  • @super 我试过static_assert,但由于某种原因Trait不会;即使它在polymorphicCall中被推断为Derived,也无法识别Derived,可以查看作为Base&lt;Derived&gt;,它没有选择正确的专业

标签: c++ perfect-forwarding static-polymorphism


【解决方案1】:

如果目标只是将polymorphicCall 的使用限制为从Base 派生的类型,您可以使用static_assert 和类型特征来做到这一点。

#include <iostream>

template <typename Derived>
struct Base
{
    decltype(auto) foo() { return static_cast<Derived&>(*this).foo(); }
};

struct Derived : Base<Derived>
{
    void foo() { std::cout << "Derived" << std::endl; }
};

template <typename T, typename = void>
struct IsDerivedFromBase : std::false_type {};

template <typename T>
struct IsDerivedFromBase<T, std::enable_if_t<std::is_base_of_v<Base<T>, T>>> : std::true_type {};


template <typename T>
struct Object
{
    template <typename U>
    Object(U&& data) : m_data(std::forward<U>(data)) {}

    T m_data;
};

template <typename T>
decltype(auto) polymorphicCall(T&& obj)
{
    using Derived = std::remove_cvref_t<T>;
    static_assert(IsDerivedFromBase<Derived>::value);
    return Object<Derived>(std::forward<T>(obj));
}

int main()
{
    Derived d;
    polymorphicCall(d);
    int i;
    //polymorphicCall(i);

    return 0;
}

【讨论】:

    【解决方案2】:

    我不确定在示例中您需要知道Derived 类型的哪个位置,但您可以创建类型特征,为每个Base 实例化提供Derived

    template <typename>
    struct derived_of;
    
    template <typename Derived>
    struct derived_of<Base<Derived>>
    {
        using type = Derived;
    };
    

    并将其与衰减的类型一起使用。

    【讨论】:

    • 试过了,它不适用于转发引用。我也用std::remove_reference做到了。
    • @PetokLorand 你能链接一个 MCVE,好吗?
    • 我更新了问题以包含更完整的代码示例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    相关资源
    最近更新 更多