Trevor Hickey
Trevor Hickey

Reputation: 37806

Does std::any employ type erasure, sub-typing, or polymorphism?

While reading the documentation on boost any and experimental any,
I did not see any mention of type erasure.

I'm trying to understand the concept of "type erasure" as it applies to C++.

Is it correct to say that the "any" type uses "type erasure" when storing its values?
Is it correct to say that it uses "polymorphism" when storing its values?
Is "type erasure" a kind of "polymorphism"?

The wikipedia article on polymorphism, also does not mention "type erasure".
It mentions "subtyping". Is "type erasure" a kind of "subtyping"?

Sorry if this question seems headed in different directions.

To summarize,

How does "type erasure", "polymorphsim", and "subtyping" relate to the any type in C++?

Upvotes: 2

Views: 1631

Answers (2)

Enlico
Enlico

Reputation: 28384

First and foremost, I would really suggest watching C++ Type Erasure Demystified - Fedor G Pikus - C++Now 2024.

With std::any you want to be able to do stuff like this:

{
  std::any a{}; // empty a
  a = 3;
  a = "hello"; // "dtor" of 3 called (yeah, not really any code running, as it's a just an int)
  a = Foo{}; // "dtor" of char const*
} // here the dtor of Foo is called

This snippet inside {…} alone tells you that the type of the object a cannot depend on the type of the object you store in it. I mean, if it I can store an object of any type in it, depending on that type means that it should depend on any imaginable type that I can define in the future, which is impossible.

Yet, the the various comments about appropriate destructors being called when needed, tell you that the code that runs when the member functions of std::any run are called, must be aware of the type they work on, because they have to know how to deal with that type, e.g. ~any() must know how to call the dtor of the held object, operator= needs to know how to do that on the lhs, and also needs to know how to make a copy of the rhs if that's an lvalue.

So what does that mean?

  • Once you've constructed a std::any, you have no compile-time information about what type is in it.
  • Yet the runtime will still access type information to do the right thing, e.g. deleting/copying/moving the std::any.

And that's what type-erasure is, so std::any must use some kind of type erasure, one way or another. Here's a very toy example of non-std::any.

But type erasure can manifest in many ways.

The symple inheritance-based polymorphism is a way of erasing types. For instance, if you are given such a function, compiled elsewhere,

std::unique_ptr<Widget> gimmeAWidget();

all you know you get from calling gimmeAWidget() is a std::unique_ptr<Widget>, and the unique_ptr layer gives you the hint that maybe (but not necessarily) you've been given an object which is of a type other than Widget but derived from it. You just don't know, and have no compile-time way of telling that. Yet, if you call a member function of Widget, e.g. gimmeAWidget()->doStuff(), you know that the right thing happens, and if gimmeAWidget() is truly giving you a derived object, then "the right thing happens" means that one way or another the runtime is recovering the type information for you.

Again, this is type-erasure.

Is it correct to say that the "any" type uses "type erasure" when storing its values?

It must use it, one way or another.

Is it correct to say that it uses "polymorphism" when storing its values?

If by "polymorphism" you mean using inheritance, no, it doesn't not need to use inheritance. Otherwise, uses "polymorphism" doesn't mean much, as it's the other way around, std::any can achieve polymorphism by using some form of type erasure.

Is "type erasure" a kind of "polymorphism"?

I'd say it's a technique that can achive runtime polymorphism. Here's an example.

Upvotes: 1

user6335390
user6335390

Reputation: 136

Is it correct to say that the "any" type uses "type erasure" when storing its values?

Maybe I and other people have a different definition of "type erasure", but using type erasure to implement any doesn't make any sense to me. If we use the definition "represent a variety of types through a single generic interface", then any, std::function, etc. fit the bill. The wikipedia definition "ensuring that the run-time execution of a program does not depend on type information" doesn't make any sense in the context of C++. Generics (the archetypal example of type erasure) doesn't mean "remove types from the language". Also, std::any (optionally) uses RTTI. So I would look elsewhere than wikipedia.

Is it correct to say that it uses "polymorphism" when storing its values?

Polymorphism is one way of implementing any. It's not required by any means, though. As far as I can tell, only Boost uses polymorphism. See this answer.

boost

Other implementations show that it's not required:

libc++

libstdc++

Also see this and the accompanying question

Is "type erasure" a kind of "polymorphism"?

No. Do you consider templates, generics, and std::function to all be polymorphism?

Upvotes: 1

Related Questions