Why are BlueprintPure and const incompatible?

UFUNCTION has the BlueprintPure parameter for tagging functions without side-effects, and C++ has the const keyword for, roughly, the same purpose. But if you try to declare a member function such as:

UFUNCTION(BlueprintPure, Category=Foos)
FVector GetFoo() const;

UHT will complain with the message:

Cannot mark function 'GetFoo' as both 'BlueprintPure' and 'const'

Why is that? Const-correct code is generally A Good Thing in C++, but presumably BlueprintPure allows Unreal to reap some sort of optimization. Am I losing anything if I just tag my const function as BlueprintCallable instead?

Edit: as of 2019 you can now mark functions as BlueprintPure=false to get an execution pin onto function call nodes for functions that are marked as const in c++:

UFUNCTION(BlueprintCallabe, BlueprintPure=false)
int32 Foo() const;

Original answer:

UFunctions marked as const are always treated as BlueprintPure, so marking a function as const and BlueprintPure is redundant, and that’s why UHT complains. There are a couple things that are debatable about this:

  1. Conflating BlueprintPure and C++'s const keyword is a little dubious because C++ const does not imply purity. const functions may modify mutable members and global state.
  2. This error could just be a warning, because it is not fatal, it is simply redundant.

Current best practice is to mark const functions as BlueprintCallable and be aware that they will be treated as BlueprintPure by the compiler.

2 Likes

Yeah, that does seem dubious. Thanks for the clarification!

Another vote for ‘dubious / do not want’ (and removed from local source)

My use case where this is not acceptable is trying to use ExpandEnumAsExecs on the output param of the const function - the BlueprintPure hides all exec pins, including the enum execs.

The function is also used from c++, where the const is required. (I could add a duplicate function, but, no.)

modified UHT:HeaderParser.cpp

-  FuncInfo.FunctionFlags |= FUNC_BlueprintPure;
+ // @ : override if any ExpandEnumAsExecs are specifically set
+ if (!MetaData.Contains("ExpandEnumAsExecs"))
+ {
+      FuncInfo.FunctionFlags |= FUNC_BlueprintPure;
+ }

(note that just removing the feature entirely will completely break a lot of built in nodes that are assumed pure due to this)

it’s not very good that this happens …
  because the call to BlueprintPure output, the function is always called on a new one and if inside the search it will also be called on a new one. In contrast to the usual BlueprintCallable, in which the output cache hangs in some magic way, and it can be taken as much as you like without calling the function again.

the case when you need to duplicate functions separately for c ++ const to be called in c ++ and separately for BP BlueprintCallable which will call the c ++ function.

There’s actually a way to get an exec pin on a const function now, just add BlueprintPure=false. Here’s an example:

UFUNCTION(BlueprintCallabe, BlueprintPure=false)
int32 Foo() const;

Notice that you have to mark the function as both BlueprintCallable and BlueprintPure=false to override the default value of BlueprintPure that we assign for const functions.

Not ideal, but this is what we settled on to maintain backward compatibility.

this is great, thanks for your reply