5

In C/C++, we can declare/define a type of pointer to function, and then declare/define some variables of this type. But I think it is ambiguity.

For example:

typedef void ( *pFunc )();
// typedef void ( aFunc )();
void theFunc() {
   cout << "theFunc has been called successfully." << endl;
};

int main() {
   pFunc pf0 = theFunc;
   pFunc pf1 = &theFunc;

   pf0();
   ( *pf0 )();
   pf1();
   ( *pf1 )();
};

Theoretically, only pFunc pf1 = &theFunc; and (*pf1)(); are legal, but all of above can pass through compilation.

In Pascal syntax, we need to define vars of function or vars of pointer to a function respectively, and the meaning of them are different and much clearer(at least I think so)!

Moreover, we can't declare/define a var of function instead of a var of pointer to function! I tried follows and get failed.

typedef void ( aFunc )();
aFunc af0 = theFunc;

If with other types such as int/double, there are very strict syntax that restricts us to use them correctly. (If int* is not same as int, why is *pf0 is same as pf0?!)

So, Can I think it is a bug of C/C++ standard?

Marco van de Voort
  • 25,628
  • 5
  • 56
  • 89
Leon
  • 1,489
  • 1
  • 12
  • 31
  • Sorry for my ugly English, I'm a Chinese. I hope I have expressed things correctly. – Leon Aug 05 '19 at 08:18
  • https://stackoverflow.com/questions/12152167/why-is-using-the-function-name-as-a-function-pointer-equivalent-to-applying-the – Andrew Kashpur Aug 05 '19 at 08:25
  • 1
    Not answering your question, but `bug of C/C++ standard` is contradictory. Bug happens when implementation doesn't comply with the standard. –  Aug 05 '19 at 08:25
  • 1
    @dyukha - Nah, there are instances where the standard**s** contain defects. This isn't the case here however. – StoryTeller - Unslander Monica Aug 05 '19 at 08:26
  • @StoryTeller, what kind of deffects? If you mean that there are bad decisions or ambiguities, it's still not a bug. –  Aug 05 '19 at 08:26
  • 4
    @dyukha - The kind that [fill entire pages](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html). Self contradictions, inconsistencies and poor specification. To call them bugs colloquially is not unreasonable. – StoryTeller - Unslander Monica Aug 05 '19 at 08:28
  • @StoryTeller, I see, thanks! –  Aug 05 '19 at 08:29
  • No. This is not a bug. This is intended and is called [function to pointer conversion](http://eel.is/c++draft/conv.func). – L. F. Aug 05 '19 at 08:32
  • 5
    Possible duplicate of [Why is using the function name as a function pointer equivalent to applying the address-of operator to the function name?](https://stackoverflow.com/questions/12152167/why-is-using-the-function-name-as-a-function-pointer-equivalent-to-applying-the) – L. F. Aug 05 '19 at 08:33
  • Sorry for using the word "BUG", because my vocabulary is poor. – Leon Aug 05 '19 at 08:35
  • pFunc is of type void (*)(). In the line pFunc myFct = theFunc, implicit casting to pFunc occurs. – mfnx Aug 05 '19 at 08:52
  • 2
    Not a bug but OP is correct in thinking that it’s inconsistent/ambiguous. These implicit conversions are mighty confusing. – Konrad Rudolph Aug 05 '19 at 10:07
  • Essentially, we could say that it is an historic decision (similar thing apply to array) where **function** decay to **function-pointer**. It was somewhat making code simpler to write by removing the need to add an & in some context. Those decision were made long before C++ was invented (reference type and templates were added latter to C++). As there are millions of line of code that now depends on that, it would be a breaking change to do that. – Phil1970 Aug 05 '19 at 14:28
  • @Phil1970 I agree with you. So, I post this question since I want to new C/C++ standard deprecate this semantics. – Leon Aug 05 '19 at 14:34
  • 1
    @Leon - Two things. (1) A SO Q&A is not how one changes the standard. One needs to send a proporsal for review to standard's committee to get changes in. (2) You missed Phil's point. Deprecating this will make existing code bases noisy with undue warnings. The committee will never make a breaking change that quite so much code depends on. This isn't like changing the meaning of `auto` or removing `register`. This is actually harmful to *all the code that is out there*. – StoryTeller - Unslander Monica Aug 06 '19 at 06:26
  • I'm pretty sure functions are just not [`objects`](https://en.cppreference.com/w/cpp/language/object) in the C++ standard way, so storing them as a value seems impossible – PeterT Aug 06 '19 at 11:11
  • 2
    @Leon: You keep referring to a "C/C++ standard". That does not exist. There's a C standard, and a separate C++ Standard. Now, C++ was designed to be compatible with C **as it existed in 1990**, but that just means C++ can compile C source code. C++ however has its entirely unique set of rules _why_ that C code is also valid in C++. – MSalters Aug 06 '19 at 11:22
  • @Leon The chance to deprecate something that is known and used by probably more of 95% of C/C++ programmer is almost nil. Instead some might recommend to always use `std::function` instead (or to always use a specific syntax for function pointer) but it will either be guidelines or specific procedure in a given development team. – Phil1970 Aug 06 '19 at 14:18
  • @Leon, While somewhat confusing, **are you aware of any problematic case** because of implicit conversion? **What benefit** changing that rule would give (other than simplifying the language)? – Phil1970 Aug 06 '19 at 15:05

3 Answers3

1

Some declared types:

// decltype of theFunc is void ()
// decltype of &theFunc is void (*) ()
// decltype of *theFunc is void (&) ()

Now, concerning your code, since a function is implicitly convertible to a pointer to that function, we have:

using pFunc = void(*)();
using rFunc = void(&)();

pFunc p_fct = &theFunc; // no conversion
pFunc p_fct = theFunc;  // conversion lvalue to pointer
pFunc p_fct = *theFunc; // conversion lvalue reference to pointer

rFunc r_fct = *theFunc; // no conversion
rFunc r_fct = theFunc;  // conversion lvalue to lvalue reference
rFunc r_fct = &theFunc; // ERROR: conversion pointer to lvalue reference not allowed

So far the conversions. Now, any object of type pFunc or rFunc is a callable object. Also, note that both (*p_fct) and (*r_fct) are of type rFunc. Therefore, you can call your function as described in the question:

p_fct();    // callable object of type pFunc
r_fct();    // callable object of type rFunc
(*p_fct)(); // callable object of type rFunc
(*r_fct)(); // callable object of type rFunc

Note that the following is equivalent to the above:

using Func = void ();
Func* p_fct = &theFunc; // no conversion
Func& r_fct = *theFunc; // no conversion
p_fct();                // callable of type Func* or pFunc
r_fct();                // callablel of type Func& or rFunc

EDIT To answer to the question: "why them was arranged in this way" from the below comment: functions cannot be copied (as explained in @JohnBurger's answer). This is why your code:

typedef void ( aFunc )();
aFunc af0 = theFunc;

doesn't work. As explained above, you can do the following though:

typedef void ( aFunc )();
aFunc* af0 = &theFunc; // or theFunc, or even *theFunc

Or you could do this:

auto myFct = theFunc;

But keep in mind that the decltype of myFct is still void (*)().

mfnx
  • 2,894
  • 1
  • 12
  • 28
  • thank you for supplementing the usage with reference to function. but my question is "why them was arranged in this way", not "how to use them". I think it is a little ambiguity and not very reasonable. Is it? – Leon Aug 05 '19 at 13:38
  • @Leon Because you cannot copy a function, as explained in John Burger's answer. – mfnx Aug 05 '19 at 13:41
  • @Leon, you could do simply auto myFct = theFunc; That looks like a variable holding a function (it is a variable holding a pointer to the function). Anyways, why do you bother so much about having a pointer to a function? What are the different meanings in Pascal, and how are they used differently? – mfnx Aug 06 '19 at 10:52
0

Ignore functions for a second: think about a struct.

You can have a struct, or a pointer to a struct:

typedef struct {
    int x;
    int y;
} Point;

Point origin = { 0, 0 };
Point here   = { 1, 1 };
Point there  = { 2, 2 };

int main() {
   Point  centre  =  origin;
   Point *myPoint = &origin;

   centre  =  here;
   myPoint = &here;

   centre  =  there;
   myPoint = &there;
} // main()

There are three global Points: origin, here and there.

main() has two variables: centre and myPoint.

centre copies the value of origin, here, and there.

myPoint points to origin, here, and there - it does not copy them.

There is a large difference between these two ideas: one copies the whole object, while the other only points to the other object.

Now think about functions. Functions are defined by the compiler in code, and can never be copied. So it makes no sense to have function variables - how large would they be? The only sensible idea is a pointer-to-function - so that is all that the C language provides.

If you want to define a C function typedef, use the following syntax:

// Fn is a function that accepts a char and returns an int
int Fn(char c);

// FnType is the type of a function that accepts a char and returns an int
typedef int FnType(char c);

// Here is the definition of Fn
int Fn(char c) {
    return c + 1;
} // Fn(c)

// Now here is how you can use FnType
int main() {
    FnType *fn = &Fn;
    return fn('A');
} // main()
John Burger
  • 3,662
  • 1
  • 13
  • 23
  • Thanks for reply! "So it makes no sense to have function variables...", I don't think so. If you are interested, you can check the relevant syntax of Pascal language. You can think a function body as a const value, and a var of function is variable what stores values of same type. Likewise, in your example, I think {0,0} is a const value, it can be stored in var "origin", and also can be stored in var "centre". So, should "aVarOfFunc = anotherVarOfFunc = aFuncBody;" mean similar semantics with "centre = origin = {0,0};"? I think we could not confuse semantics and implementation. – Leon Aug 05 '19 at 14:27
  • Syntax is about semantics, while "var stores the address of function in fact" is about implementation, aren't they? – Leon Aug 05 '19 at 14:39
  • @Leon: "Function bodies as values" makes perfect sense. But as John points out, these "values" do not have a fixed size. That's why you can have a pointer to the start of them, but you can't have a generic function variable. Note that in C++ and old C code, `sizeof(x)` is a constant that just depends on the type of `x`. – MSalters Aug 06 '19 at 11:28
0

I think that I have found the answer.

Indeed c++ standard has offered a method to declare a type of function without the help of pointers.

example:

#include <functional>

using AFunc_t = function<void( int )>;

void theFunc( int );

AFunc_t afunc = theFunc;

I hope this could help someone.

Leon
  • 1,489
  • 1
  • 12
  • 31