Home > Blockchain >  What would cause a C object's type information to change after it is returned from a function
What would cause a C object's type information to change after it is returned from a function

Time:10-05

Introduction

I have C 17 code of the following form:

Base& makeDerived(int val) {
  std::shared_ptr<Derived> derived = secondLayer(val);
  std::cout << typeid(derived).name() << std::endl;
  return *derived;
}

int main(void) {
    Base& derived = makeDerived(7);
    std::cout << typeid(derived).name() << std::endl;

    return 0;
}

I can get this code to either print:

Derived
Derived

or

Derived
Base

with different implementations of Derived and Base, but I don't understand why this would happen.

Supplemental Notes

The actual code I am basing this question on prints: Derived, Base but it would be much too long to paste, and I wouldn't be authorized to do so. I can't figure out how to distill it down to a characteristic example, all efforts seem to print Derived, Derived, and the fact that I can't distill it down is basically what my problem is because I don't understand why it would be different.

In all implementations I care about, Derived is a descendant of Base.

Question

What would cause a descendant of a base class to change its type information to the base class itself after exiting a function?

CodePudding user response:

It is not clear how you can get output of Derived Derived or Derived Base, because derived is a std::shared_ptr<Derived>. Anyhow, here is a minimal example with the same issue as your code (most likely):

#include <iostream>
#include <memory>

struct Base { virtual ~Base() = default; };
struct Derived : Base {};

std::shared_ptr<Derived> secondLayer(int) { return std::make_shared<Derived>(); }

Base& makeDerived(int val) {
  std::shared_ptr<Derived> derived = secondLayer(val);
  std::cout << typeid(derived).name() << std::endl;
  return *derived;
}

int main(void) {
    Base& derived = makeDerived(7);
    std::cout << typeid(derived).name() << std::endl;
}

possible output:

St10shared_ptrI7DerivedE
4Base

I was planning to put a disclaimer here on how you should pay attention to warnings and use the compilers instrumentation capabilities. However, gcc silently swallows this code with -Wall -Werror and neither -fsanitize-address nor -fsanitize-undefined do help. I would consider this as a bug.

However, return *derived is returning a reference to an object that gets destroyed once the function returns. Using that reference in main causes undefined behavior.

CodePudding user response:

You can read more about this answer on the following link: https://en.cppreference.com/w/cpp/language/typeid

The implementation of Base and Derived class can be made using different approaches, and what can do a difference in the use of typeid is the polymorohic or non polymorphic inheritance.

A class derived from a base class that owns virtual methos is considered a polymorphic inheritance. In case of a polymorphic inheritance typeid is able to recognize the real type of the instance (even in your case, where you are casting it in a reference to Base class).

A class derived from a base class that doesn't own any virtual mehotd is considered a non polymorphic inheritance and typeid can't recognize the real type of the instance you are using, and in your case will return a std::type_info that refer to the type Base&.

CodePudding user response:

You create a shared ptr. Then you return a reference and destroy the last reference.

Inspecting the value through the reference is going to be undefined behaviour.

So you'll get any behaviour at all. Sometimes what you want, sometimes not.

  •  Tags:  
  • c
  • Related