Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static polymorphism with forwarding references

I am trying to use static polymorphism like in the simple example shown below.

#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;
}

The problem is that T in polymorphicCall is deduced as Derived, this way anything can be passed to that function, even int. Is there a way to only accept Base<Derived> types?

I tried using a forwarding reference and an enable_if on the template parameter, but then i can't deduce the Bases template parameter.

Is there any way to use both forwarding references and static polymorphism?

EDIT: Updated the code example to include the actual forwarding reference and how a try to use it.

The error shown is: "error: invalid use of incomplete type 'struct Traits'"

Link: https://godbolt.org/z/3EcS47

like image 532
Petok Lorand Avatar asked Mar 11 '26 11:03

Petok Lorand


1 Answers

If the goal is just to limit the use of polymorphicCall to types derived from Base you can do that with a static_assert and a type trait.

#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;
}
like image 196
super Avatar answered Mar 13 '26 01:03

super



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!