Why Foo f(Bar()); can be a declaration of a function that takes type Bar and returns type Foo?
The function f
actually takes a function pointer to a function that takes no arguments and gives a Bar
. The type of the argument to f
is Bar (*)()
.
This code fails to compile (and we can see the actual type of the argument in the error message):
class Foo { };
class Bar { };
Foo f(Bar());
int main() {
Bar b;
f(b);
return 0;
}
But this code does compile:
class Foo { };
class Bar { };
Foo f(Bar());
Bar g();
int main() {
f(g);
return 0;
}
The second meaning it could have, as you say in the question, is that you are making a new Foo
object called f
and you are calling the constructor with Bar()
(a new instance of Bar
). It would be similar to:
Foo f = Foo(Bar());
In this situation of Foo f(Bar());
though, the first interpretation is chosen by the compiler.
Somewhat confusingly, if you add another set of parentheses, as in
Foo f((Bar()));
the compiler picks the second interpretation.
Syntax of an un-named function pointer in C++
Fully explicit form:
Foo bar(Baz f());
bar
is a function that takes a single parameter f
, which is a function (taking no arguments) returning Baz
.
Without naming the parameter:
Foo bar(Baz ());
The reason bar
ends up taking a pointer to a function is that functions cannot be passed by value, so declaring a parameter as a function automatically decays it into a pointer. The above declaration is equivalent to:
Foo bar(Baz (*)());
// or:
Foo bar(Baz (*f)()); // with a named parameter
This is similar to void foo(int [10])
where int [10]
also means int *
in a parameter list.
How is this a most vexing parse?
Rectangle s(origin());
is a vexing parse too. It declares a function s
which returns rectangle
, and takes as argument pointer to function returning origin
. Not sure what you meant by "works fine".
rectangle w( origin(), extents() );
declares a function w
returning rectangle
and taking arguments: pointer to function returning origin
, and pointer to function returning extents
.
For more detail see this question or browse the other questions under the most-vexing-parse tag.
What is this member pointer syntax?
struct Trait<T U::*>
^^^^^^ // I don't understand this syntax
The above syntax means that we've a pointer to a member of a class named U
where the member has type T
. In other words, a pointer to a member of class U
that has type T
.
Now let's apply this to &Foo::bar
. The type of the expression &Foo::bar
is:
void (Foo::* f)()
Now you can compare this with T U::*
So, after comparing we get:
U = Foo
which is the class type.T = void()
which means a function that has the return typevoid
and takes no parameter i.e., a function type.
which effectively means that we've a pointer to a member function that has the return type void
and takes no parameters, of class Foo
.
Keep type hints for a TypeScript function that can take any arguments
Your Foo
type accepts any string
-returning function. But when you assign a value to a variable of type Foo
, the compiler does not track the particular value you've assigned. In some sense the compiler treats alpha
, bravo
, and charlie
, as opaque boxes labeled Foo
, and that's all it knows about the contents. So while it's really easy to provide a value of type Foo
, it's almost impossible to consume one.
This forgetfulness is the general behavior for variables of non-union types. For union types, there is assignment narrowing where the appearent type of a variable will be narrowed based on the inferred type of the assigned value. That lets you write const x: string | undefined = "hello"; x.toUpperCase();
without error, since x
is narrowed from string | undefined
to string
by the assignment. It would be nice if this worked in some way for non-unions, and there is a longstanding request at microsoft/TypeScript#16976 to do this, but I don't see any indication this will ever be implemented (it would be a big breaking change to do so). You can't plausibly represent Foo
as a union type, and unions of functions have other complicating behavior anyway, so this is not a feasible solution for you.
Instead, my suggestion is that you really want something like the so-called satisfies
operator, as discussed in microsoft/TypeScript#47920 and originally microsoft/TypeScript#7481. You want alpha
and bravo
and charlie
to satisfy the Foo
type without being widened to the Foo
type. There's no built-in operator that works this way, but you can implement a generic identity helper function that gives you this behavior (this workaround is discussed in those GitHub issues):
type Foo = (...args: never) => string
const asFoo = <F extends Foo>(f: F) => f;
Instead of annotating variables like const x: Foo = ...
, you use the helper function like const x = asFoo(...)
. Here's how it works:
const alpha = asFoo(() => `no arguments`);
console.log(alpha()) // okay
const bravo = asFoo((a: string) => `uses one argument${a}`);
bravo() // error, argument of type sting expected
const charlie = asFoo((a: number, b: string) => `uses two arguments: ${a} and ${b}`);
charlie() // error, two arguments expected
Looks good!
Note that if you actually want to use Foo
objects programmatically you will need to keep track of the arguments list types by using generics, like this:
type GenericFoo<A extends any[]> = (...args: A) => string;
function useFoo<A extends any[]>(foo: GenericFoo<A>, ...args: A): string {
return foo(...args);
}
useFoo(alpha); // okay
useFoo(bravo, "a"); // okay
useFoo(bravo, 123); // error, number is not string
useFoo(bravo); // error, expect another argument
Playground link to code
Is there a technical reason for not allowing a using declaration to make a function name accessible under a different name?
See N1489:
It is possible to generalize the notion of alias beyond types and
namespaces to functions, variables, etc. We do not see sufficient
benefits from doing this and can imagine serious overuse leading to
confusion about which functions and variables are used. Consequently,
we do not propose the generalizations mentioned in this section.
Furthermore, we do not plan to work further on these generalizations
unless someone comes up with examples that indicate significant
usefulness.
Related Topics
Why Must Virtual Base Classes Be Constructed by the Most Derived Class
Can Void* Be Used to Store Function Pointers
Does Copy List Initialization Invoke Copy Ctor Conceptually
C++ Static Const Access Through a Null Pointer
Glut Deprecation in MAC Osx 10.9, Ide: Qt Creator
Efficient Multiply/Divide of Two 128-Bit Integers on X86 (No 64-Bit)
Why "Not All Control Paths Return a Value" Is Warning and Not an Error
Auto' as a Template Argument Placeholder for a Function Parameter
What's the Difference Between Parentheses and Braces in C++ When Constructing Objects
Difference Between Regex_Match and Regex_Search
Remote Debugging C++ Applications with Eclipse Cdt/Rse/Rdt
What Is a Good Naming Convention for Vars, Methods, etc in C++