Search code examples
c++unreal-engine4unreal-development-kit

Constructor Asking for Wrong Number of Arguments


I have build a class in Unreal called Groundmode

GroundMode.h: #pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Components/BoxComponent.h"
#include <NMR\GroundDirection.h>
#include "GroundMode.generated.h"

UCLASS()
class NMR_API UGroundMode : public UObject
{
    GENERATED_BODY()

    public:
        UGroundMode();   
};

GroundMode.cpp:

#include "GroundMode.h"

UGroundMode::UGroundMode() {}

I want to be able to instantiate this class in another class. PlayerPhysicsCollisionInfo.h

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GroundMode.h"
#include "PlayerPhysicsCollisionInfo.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class NMR_API UPlayerPhysicsCollisionInfo : public UActorComponent
{
    GENERATED_BODY()

public: 
    UPlayerPhysicsCollisionInfo();

    UGroundMode downMode;
    UGroundMode upMode;
    UGroundMode leftMode;
    UGroundMode rightMode;
};

PlayerPhysicsCollisionInfo.cpp: #include "PlayerPhysicsCollisionInfo.h"

    void UPlayerPhysicsCollisionInfo::BeginPlay()
    {
        Super::BeginPlay();
        downMode = new UGroundMode();
        upMode = new UGroundMode();
        leftMode = new UGroundMode();
        rightMode = new UGroundMode();
    }

When I attempt to create instances of UGroundMode with the new keyword I get the following compile error: D:\Documents\Unreal Projects\NMR\Source\NMR\PlayerPhysicsCollisionInfo.cpp(25) : error C2661: 'UGroundMode::operator new': no overloaded function takes 1 arguments

What am I doing wrong? The constructor takes no arguments not 1.


Solution

  • The new expression, like you're doing here, does two things.

    1. It allocates memory for the object by calling an appropriate allocation function for that object.
    2. It constructs the object in that allocated memory.

    When it calls the allocation function for a class T, it first determines the size of that class, std::size_t size, and passes that as the first parameter to the allocation function. The allocation function for a class is the first resolved operator new() function available to a class within its scope. Any optional placement params are then sent as the next parameters to the allocation function. Placement params are parentheses-enclosed expressions immediately after the new keyword:

    T* foo = new T(); // good old fashioned uncomplicated new
              ^
              |
              +---- wants to call a `operator new(std::size_t)` function
    
    T* bar = new(1, 2) T(); // a little more complicated
              ^    ^
              |    |
              |    +----- these are placement params
              +---------- wants to call `operator new(std::size_t, int, int)`
    

    Your issue is when the new expression tries to do the allocation and finds its appropriate operator new(). Classes can override operator new(), which your class does. The new keyword will take sizeof(T), and attempt to send that as the first parameter to the class-defined operator new function.

    Where is that class-defined overloaded operator new() function, you ask?

    Right here

    UObject, which your class inherits from, has a void* UObject::operator new() function declared that takes five, count 'em FIVE, parameters.

    The new keyword identifies this operator new() as your class' operator new(), but sees that you're only passing the one parameter (sizeof(T)), and fails.


    The solution

    As described on the linked Unreal page, new expressions are not to be used with UObject objects. Use StaticConstructObject() instead.


    If you want to call the global operator new anyway

    If you want to make Unreal engine even unrealer, you can specify an appropriate operator new function by explicitly scoping it. You can attempt to access the global operator new() function by putting the global scope indicator at the front of you new statement:

    downMode = ::new UGroundMode();
    

    Of course, then you'll run into the problem that ::new UGroundMode(), like any other new operation, returns a UGroundMode* which you're trying to store in a UGroundMode object. This will fail. I don't know what it will do the Unreal engine. Probably bad things.


    Further reading

    https://en.cppreference.com/w/cpp/language/new
    https://en.cppreference.com/w/cpp/memory/new/operator_new