Home > Blockchain >  Cannot construct an instance of a template class, within a member template function of that template
Cannot construct an instance of a template class, within a member template function of that template

Time:06-09

I have a template class defined as so:

template<typename T> class C_SharedResource : public C_Instance
{
    // ...
private:
    std::shared_ptr<T> g_resource;
    S_AutoDestructionData g_autoDestructionData{};

    // ...
public:
    C_SharedResource() {}
    C_SharedResource(const T& in_resource, S_AutoDestructionData in_autoDestructionData = { false, false });
    C_SharedResource(const std::shared_ptr<T>& in_resource, S_AutoDestructionData in_autoDestructionData = { false, false });
    template<typename T2> C_SharedResource<T2> dynamic_cast_resource() const;

    // ...
}

With the definition of dynamic_cast_resource being:

template<typename T>
template<typename T2>
C_SharedResource<T2> C_SharedResource<T>::dynamic_cast_resource() const
{
    T2* tempResourcePtr = dynamic_cast<T2*>(g_resource.get());
    if (tempResourcePtr == nullptr)
    {
        return C_SharedResource<T2>();
    }
    
    // This lines causes the compiler error.
    C_SharedResource<T2> tempSharedResource(std::dynamic_pointer_cast<T2>(g_resource), g_autoDestructionData);

    return tempSharedResource;
}

The idea behind the function is to down cast the stored shared pointer, and then return a newly constructed shared resource with the appropriate derived type.

An example of it being used:

// C_ITransformEntityComponent publicly inherits from C_IEntityComponent, which is also virtual. 
std::shared_ptr<C_ITransformEntityComponent> transformComponent = std::make_shared<C_ITransformEntityComponent>();

C_SharedResource<C_IEntityComponent> sharedTC_1(transformComponent);
C_SharedResource<C_ITransformEntityComponent> sharedTC_2;
C_SharedResource<C_ITransformEntityComponent> sharedTC_3 = sharedTC_1.dynamic_cast_resource<C_ITransformEntityComponent>();

However the line:

C_SharedResource<T2> tempSharedResource(std::dynamic_pointer_cast<T2>(g_resource), g_autoDestructionData);

Fails to compile, with the error:

Error C2664 'C_SharedResource<C_ITransformEntityComponent>::C_SharedResource(const T &,C_SharedResource<T>::S_AutoDestructionData)': cannot convert argument 1 from 'std::shared_ptr<C_ITransformEntityComponent>' to 'const T &'

I am using Visual Studio 2022, with the C standard set to C 20.

Edit: Minimal reproducible example:

#include <iostream>
#include <memory>

class C_Instance
{
public:
    virtual void some_func() {}
};

template<typename T> class C_SharedResource : public C_Instance
{
public:
    struct S_AutoDestructionData
    {
        bool m_autoDestroyAllInstances = false;
        bool m_transferAutoDestructionState = false;
    };

private:
    std::shared_ptr<T> g_resource;
    S_AutoDestructionData g_autoDestructionData{};

public:
    C_SharedResource() {}
    C_SharedResource(const T& in_resource, S_AutoDestructionData in_autoDestructionData = { false, false });
    C_SharedResource(const std::shared_ptr<T>& in_resource, S_AutoDestructionData in_autoDestructionData = { false, false });
    template<typename T2> C_SharedResource<T2> dynamic_cast_resource() const;

    void some_func() override {}
};

class C_IEntityComponent
{
public:
    virtual void some_other_func() {}
};
class C_ITransformEntityComponent : public C_IEntityComponent
{
public:
    void some_other_func() override {}
};

template<typename T>
template<typename T2>
C_SharedResource<T2> C_SharedResource<T>::dynamic_cast_resource() const
{
    T2* tempResourcePtr = dynamic_cast<T2*>(g_resource.get());
    if (tempResourcePtr == nullptr)
    {
        return C_SharedResource<T2>();
    }

    C_SharedResource<T2> tempSharedResource(std::dynamic_pointer_cast<T2>(g_resource), g_autoDestructionData);

    return tempSharedResource;
}

int main()
{
    std::shared_ptr<C_ITransformEntityComponent> transformComponent = std::make_shared<C_ITransformEntityComponent>();
    C_SharedResource<C_IEntityComponent> sharedTC_1(transformComponent);
    C_SharedResource<C_ITransformEntityComponent> sharedTC_2;
    C_SharedResource<C_ITransformEntityComponent> sharedTC_3 = sharedTC_1.dynamic_cast_resource<C_ITransformEntityComponent>();
    return 0;
}

CodePudding user response:

You get the compiler error because struct S_AutoDestructionData is a nested type:

template<typename T> class C_SharedResource : public C_Instance
{
public:
    struct S_AutoDestructionData
    {
        bool m_autoDestroyAllInstances = false;
        bool m_transferAutoDestructionState = false;
    };

    // ...

    C_SharedResource(const T& in_resource, S_AutoDestructionData in_autoDestructionData = { false, false });
    //                                     ^ C_SharedResource<T>::S_AutoDestructionData
 
    C_SharedResource(const std::shared_ptr<T>& in_resource, S_AutoDestructionData in_autoDestructionData = { false, false });
    //                                                      ^ C_SharedResource<T>::S_AutoDestructionData

     // ...
};

So, in this line:

// T  = C_IEntityComponent
// T2 = C_ITransformEntityComponent
C_SharedResource<T2> tempSharedResource(std::dynamic_pointer_cast<T2>(g_resource), g_autoDestructionData);

C_SharedResource<T2>'s constructor expects a C_SharedResource<T2>::S_AutoDestructionData as the second argument; while you're passing a C_SharedResource<T>::S_AutoDestructionData. Those are not the same type.

If you pull struct S_AutoDestructionData out from class C_SharedResource:

struct S_AutoDestructionData
{
    bool m_autoDestroyAllInstances = false;
    bool m_transferAutoDestructionState = false;
};

template<typename T> class C_SharedResource : public C_Instance
{
public:

     // ...
};

struct S_AutoDestructionData wont be nested anymore and your code will compile.

  • Related