0

I stumbled upon this strange behaviour.


#include <iostream>
#include <functional>

struct adder {
  auto add(int x, int y) -> int {
    return x + y;
  }
};

auto main() -> int {
  std::cout << std::invoke(&adder::add, *static_cast<adder*>(nullptr), 1, 2) << '\n';
  return 0;
}

It was compiled with gcc version 11.3.0 on Ubuntu Ubuntu1 22.04

When i run it, it returns 3.

I understand why it is possible since adder, at this point, is more or less just like a namespace. And when I add some state to the struct I get a segmentation fault like expected.

Is this part of the standart or maybe just a gcc thing?

Symlink
  • 383
  • 2
  • 12
  • 8
    `*static_cast(nullptr)` is undefined behavior. The output of the code could be anything. "it appears to work" is actually the worst of all outcomes – 463035818_is_not_an_ai Aug 02 '23 at 08:52
  • 1
    a class without members is not like a namespace. You could make the function `static`, perhaps it should be when it makes no use of members, but then prefer to make it non-member – 463035818_is_not_an_ai Aug 02 '23 at 08:53
  • 2
    `adder`is not callable so this wouldn't compile. You need to call the `add` member function instead of using `std::invoke`. – interjay Aug 02 '23 at 08:54
  • 1
    i wanted to ask, just for my culture, why does it even work to call `adder`? it looks to me like you're not calling `adder::add`, but just `adder`. – Victor Aug 02 '23 at 08:55
  • 2
    you were compiling and running different code not the one you posted here. https://godbolt.org/z/YW1sn9d4G – 463035818_is_not_an_ai Aug 02 '23 at 08:56
  • 1
    It's a common pattern in the C++ Library to create a empty `struct` with a member function to inject behaviour (see the example in https://en.cppreference.com/w/cpp/container/unordered_map/unordered_map). What you should do is create an instance and call the member. You can expect a good compiler to avoid any imagined overhead. – Persixty Aug 02 '23 at 09:05
  • 1
    This code does not compile on my machine, because I have warnings enabled, and treat-warnings-as-errors. If I take the safety off my compiler so the code compiles anyway, this code **crashes** on my machine. That crashing behavior complies with **undefined behavior**, because any observed behavior is acceptable. I'm just glad the behavior of the program did not email my browser history to my grandmother. *whew* – Eljay Aug 02 '23 at 11:27
  • @Persixty Typically (as in the example on your linked page) the member function is the `operator()` so that an instance of the `struct` is a callable. – Sebastian Aug 02 '23 at 12:38
  • @Sebastian Yes. That is an operator but I believe the same notions apply. You shouldn't call a operator on a null object anymore or less than a conventional member function. You should create an instance (albeit an empty stateless `struct`) and call the operator (or member function) on that. – Persixty Aug 02 '23 at 13:17
  • @Persixty Definitely, one should *never* dereference null objects or call their member functions. BTW `operator()` is one of several possible operators. – Sebastian Aug 03 '23 at 06:33

0 Answers0