2

I saw the following code in this question:

class CFoo
{
   int a;
public:
   CFoo():a(1){}
   ~CFoo(){}
   getNum(){return a;}
};

void tfunc(void* data)
{
    CFoo* foo = static_cast<CFoo*>(data);
    std::cout << "Number: " << foo->getNum();
    delete foo;
}

int main()
{
   CFoo* foo = new CFoo;
   void* dt = static_cast<void*>(foo);
   tfunc(dt); // or tfunc(static_cast<void*>(food));

   return 0;
}

and started to wonder about why you would convert a pointer to a type to a pointer to void instead of simply assign the void pointer the the actual pointer. Like when calling tfunc in the code code above, he could have called it like tfunc(&foo) instead of converting the pointer to a type to void pointer as he did using the static cast static_cast<void*>(foo);, right?

  • 2
    You probably mean `tfunc(foo)` . `tfunc(&foo)` does something else entirely, and in particular would exhibit undefined behavior. Taking a step back, it's not clear why `tfunc` takes `void*` in the first place, and not `CFoo*`. – Igor Tandetnik Jun 09 '17 at 20:47
  • @IgorTandetnik, yep. It has been edited. Well, yeah a little confusing since it will be converted back to CFoo anyway, but that is irreverent for this question, I guess :))) –  Jun 09 '17 at 20:50
  • `tfunc(foo)` is equivalent to `tfunc(static_cast(foo))`, if that's what you are asking. – Igor Tandetnik Jun 09 '17 at 20:53
  • In the context of C++, using `void*` should be incredibly rare. However, in C it is extremely common and I can give many examples. You often end up using C libraries from C++ so... – Nir Friedman Jun 09 '17 at 21:03
  • @IgorTandetnik yes but why would he do `tfunc(static_cast(foo))` or `tfunc(foo)` instead of just `tfunc(foo)`? –  Jun 09 '17 at 21:06
  • What do you mean, `tfunc(foo)` instead of `tfunc(foo)`? You are repeating the same thing twice. – Igor Tandetnik Jun 09 '17 at 21:08
  • you should not ask two questions in one post. Ask two separate questions. – bolov Jun 09 '17 at 21:14
  • @IgorTandetni, the first time i said tfunc(foo), I meant `tfunc(dt);` –  Jun 09 '17 at 21:16
  • @bolov Understood. –  Jun 09 '17 at 21:17

2 Answers2

2

The wording of your question is a bit confusing, you are not actually asking why converting to void*, you are asking why use explicit casting instead of rely on implicit casting, i.e.

tfunc(static_cast<void*>(food)); // explicit casting

vs

tfunc(food); // implicit casting

The issue is a bit more general than casting to void*. Unfortunately C++ allows a fair amount of implicit dangerous castings. E.g. between singed and unsigned, from a wider integer or floating point to a narrower one, conversions to bool and conversions to void*. All this conversions have the potential to silently introduce bugs in the code. c++11 did a little step in the right direction by not allowing narrowing in the new uniform initializer syntax {}, but due to backward compatibility all previous implicit casts are still allowed.

That's why explicit casting is encouraged. It helps showing the intent of the writer. It shows that the conversion is explicitly desired, as opposed to happening silently, possibly without the author knowledge. Also very important, it helps on code review or when reading the code by being a flag sort of "Here there is cast. Be aware of that when you look over this code"

bolov
  • 72,283
  • 15
  • 145
  • 224
1

From the example above I do not understand why the signature of function tfunc is void tfunc(void* data) and not void tfunc(class CFoo* data).

Anyway, it is valid to convert a pointer to any type T into a void *, and it is valid to convert such a pointer back to T* later. The conversion from T* can be implicit, whereas conversion from void* back to T* needs to be explicit. So, in your example, tfunc(foo) is equivalent to tfunc(static_cast<void*>(foo)) and to tfunc(dt).

Concerning the point "conversion of CFoo* to void*, there is a standard conversion (cf. this c++ online draft standard):

4 (1) Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions.

4.10 Pointer conversions (2) A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a non-null pointer value of a pointer to object type to a “pointer to cv void” represents the address of the same byte in memory as the original pointer value. ...

I'd prefer the implicit conversion over explicit conversions, as the the built-in meaning of the (implicit) standard conversion meets exactly the requirements.

A use case for void* in a function signature could be that one wants to pass objects of unrelated type and decide inside the function to which type it has to be casted back. Suppose, for example, a function handling callbacks for different response types, and each response type might pass different (class hierarchically independent) objects:

void someCallback(int responseType, void *context) {
  if(responseType == 1) {
    CFoo1* foo1 = static_cast<CFoo1*>(context);
    ...
  }
  else {
    CFoo2* foo2 = static_cast<CFoo2*>(context);
    ...
  }
}
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • Okay, thank you! But why would he do `tfunc(static_cast(foo))` or `tfunc(dt)` instead of just `tfunc(foo)`? Or did you already answer that? –  Jun 09 '17 at 21:28
  • Ahh okay thank you so much! So about that specific question, is it more about what you prefer since all are equivalent? –  Jun 09 '17 at 21:46
  • @OfT: yes, what you prefer. The implicit conversion from any `T*` to `void*` is a rather old language construct. I personally prefer this implicit one, since introducing ` – Stephan Lechner Jun 09 '17 at 21:49
  • @OfT: also added a reference to the standard, which defines that for `T*` to `void*` is an implicit conversion with exact the required meaning exists. – Stephan Lechner Jun 09 '17 at 22:15