I often need to define an empty object in C++, which is typically a structure without any members or methods:
struct Dummy {};
Reincarnations of the same idea include classes like nill_t
, null_t
, Empty
, None
etc.
I was wondering if there is a standard library object that fits this description (empty class, used as tag for "nothing" or "empty") or a canonical way to define it (proper name etc). One idea would be to use std::false_type
but unfortunately this class is far from empty.
Edit:
Ideally I'd use the Standard None type as a (n empty) base to have both the rich name description and hide implementation (or use it as is, where it makes sense)
struct Dummy : std::none {};
struct Noop : std::none {};
Having the language take care of intricasies of "nothingness" like the one @Barry mentions in his answer, or e.g. providing an equality operator that always returns false
(like NaN) or whatever the language considers "proper behavior" for "nothing" would be nice.
The question merely asks whether there's a standard None or Empty or Nothing type, I never suggested obfuscating the code by using it where it doesn't make sense.
The main difference between null and empty is that the null is used to refer to nothing while empty is used to refer to a unique string with zero length.
The following entities are not objects: value, reference, function, enumerator, type, non-static class member, template, class or function template specialization, namespace, parameter pack, and this.
The C++ standard library actually has a large number of "empty" types. std::monostate
, std::nullopt_t
, the std::in_place_t
tag types, etc. They all have different names because they are used for different purposes.
You should not have one all-purpose "empty" type, because seeing the name there will tell you what the code is doing. std::optional<T>(std::in_place, ...)
communicates a lot more about what is going on than std::optional<T>(std::monostate{}, ...)
.
Indeed, if a single type were used, you wouldn't be able to differentiate between std::optional<T>(std::nullopt)
and std::optional<T>(std::in_place)
. These are two very different function calls.
The name you use should be whatever makes the code which uses it readable.
As for canonical conventions, the C++ standard uses certain conventions based on how you're using it. Types that the user is expected to type directly into their code, like std::monostate
, are named normally. You're expected to create variant
s that use monostate
in the type.
Tag types, used to select constructor or operator()
overloads, will typically end in _t
. But they also have an inline constexpr
variable defined for them that doesn't end in _t
. For example, std::nullopt_t
is the name of the type, while std::nullopt
is the name of a variable of that type.
This is particularly important, as std::nullopt_t
's cannot directly be constructed by the user. You can copy std::nullopt
instances around, but you cannot create a std::nullopt_t
instance directly.
That last bit is something the standard is a bit inconsistent about. Most tag types don't have this requirement; you can invoke std::in_place_t{}
all you like. But std::nullopt_t
does.
Examples from your comment:
as the default tag in struct templates, as the default tag in function templates
I don't know what a "default tag" is in this context, but I would probably call it default_t
. Since that spells out the meaning of the thing.
as the "no output" tag in a processing pipeline
I would spell this void
or if that's not an option, drop
or discard
.
as the equivalent of Python's None in some generic code
That already has a name: nullptr
. If it is a non-pointer that may or may not be present, it's spelled std::optional<T>
. And if you really are in generic code, std::nullopt
itself works as a way to indicate that the user conceptually provided an empty optional
.
These are different uses with different interpreted meaning. Therefore, they should not have the same name even if they're all implemented as equivalent types.
The best practice is to do it this way:
struct Dummy { explicit Dummy() = default; };
Which you can then create an instance of:
inline constexpr Dummy dummy;
The advantage of this is that Dummy
is not an aggregate (in C++20), which prevents this from compiling:
void f(Dummy);
f({}); // error
f(Dummy{}); // ok
f(dummy); // ok
Which is important because the point of tag types is visibility, and {}
is... not that.
I was wondering if there is a standard library object that fits this description
There are quite a few standard library empty tag types (e.g. std::nullopt_t
), but if you need a tag type, you need it for a particular reason and reusing an existing tag just to avoid creating a new type doesn't seem like a good tradeoff. I mean, use std::nullopt_t
if that makes sense for what you're doing (perhaps you're doing something equivalent to optional
and so that's a reasonable tag to use for this problem) but don't just use std::nullopt_t
because... you need a tag and that's available.
C++17 introduced std::monostate
. It is meant to be used with std::variant
but also fits nicely "empty class, used as tag for "nothing" or "empty"".
It comes with a couple of related free functions that enable most common comparisons, but otherwise is nothing more than:
struct monostate { };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With