Skip to content

Value Types and Abstracts

Andrew Lind edited this page Jun 18, 2026 · 2 revisions

Value classes

A class can be emitted as a C++ value type rather than a reference type: new Foo(a) becomes a stack value Foo(a) (no heap), a field/Array<Foo> is held by value (Foo/std::vector<Foo>), member access is ., the destructor is non-virtual (no vtable — a flat value layout), and the ownership analysis never touches it (nothing to free). This gives a value object with methods — the hand-written-C++ idiom of a small struct-with-behaviour — letting value-heavy code (vectors, JSON-style trees) transliterate by value instead of being reimplemented on the heap.

A value type can't do C++ polymorphism, so inheritance is rejected (slicing), as is a field holding the class itself by value (an incomplete type — use an Array<Self>); Null<Foo> still boxes to Foo*, as for any value type.

Two tags select a value class:

  • the real hxcpp @:stackOnly metadata makes one that also obeys hxcpp's stack-residence rule — it may not be nested as a field or element (Hatchet flags that, steering to an abstract) — suiting genuine stack-only locals while staying portable to hxcpp; and
  • an abstract Name(U) newtype (below) is a value type that nests freely, the portable way to get a value object with methods, operators, and conversions.

See the Metadata page for the full @: vs @ split.

Abstract types (abstract Name(U))

Haxe's newtype: a value type with methods over an underlying type U, lowered to a value class that wraps U in a synthetic __this field. Inside its methods Haxe this is the underlying value (so this = v and this.field operate on U), and new Name(...) is value construction. This is the portable, idiomatic way to get a value object with operators and conversions, and the nestable value type (a field, an Array<Name>, or a recursive-by-value tree through a container).

Supported member metadata:

  • @:op(...) operator overloading, emitted as a C++ operator that forwards to the named method (so the value reads as v[k] / a + b and v.method(...), with C++ doing the operator resolution): @:op([]) (one-arg read) → operator[], @:op(A op B) → the binary operator op, and prefix unary @:op(-A) / !A / ~A. Forms with no C++98 mapping — the two-arg @:op([]) write, @:op(a.b), @:op(a()), postfix — are flagged as unsupported.
  • @:to → an implicit C++ conversion operator (@:to function toStr():Stringoperator std::string()), and @:from (a static factory) → a converting constructor, so the source type implicitly converts to the abstract.

Generic abstracts and @:multiType are not supported; an abstract's value-class nature means the usual value-class caveats (no polymorphism; nests freely; Null<Name> boxes to Name*).


Related: Container Semantics for when value semantics are what you want, and Diagnostics for the generic / @:multiType / unsupported-@:op flags.

Clone this wiki locally