pointer to function as static member in structure - c++

struct X {
int f(int);
static int f(long);
};
int (X::*p1)(int) = &X::f; // OK
int (*p2)(int) = &X::f; // error: mismatch
int (*p3)(long) = &X::f; // OK
int (X::*p4)(long) = &X::f; // error: mismatch
int (X::*p5)(int) = &(X::f); // error: wrong syntax for pointer to member
int (*p6)(long) = &(X::f); // OK
I think that p1 and p5 is the same case. Why is p5 wrong?

Because the standard says so. From the N3936:
5.3.1 Unary operators
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [ Note:
that is, the expression &(qualified-id), where the qualified-id is
enclosed in parentheses, does not form an expression of type “pointer
to member.” Neither does qualified-id, because there is no implicit
conversion from a qualified-id for a non-static member function to the
type “pointer to member function” as there is from an lvalue of
function type to the type “pointer to function” (4.3). Nor is
&unqualified-id a pointer to member, even within the scope of the
unqualified-id’s class. — end note ]

The C++ Standard's definition of the built-in operator & states that only when the parameter to & is a qualified-id, meaning something like Class::Member, does & result in a pointer-to-member. The parentheses make it no longer a qualified-id, so it attempts to parse X::f directly, which is illegal in this context: you're assigning an int (*)(long) to an int (X::*)(int).
The distinction between the two cases resolves an ambiguity. Let's say that you have:
struct X {
int m;
};
struct Y {
int m;
};
struct Z : X, Y {
void F();
};
void Z::F() {
int X::*p1 = &X::m;
int *p2 = &(X::m);
}
Here, &X::m is a pointer-to-member, whereas &(X::m) is an ordinary pointer to int, using the X:: qualification to resolve the ambiguity between X's m and Y's m.

Related

ADL in noexcept, who's wrong, who's right? [duplicate]

I have some code which requires me to use *this, but I want it to be noexcept friendly:
struct foo;
// Would actually be something with conditional noexcept
void do_something(foo&);
struct foo {
void fn()
noexcept(noexcept(::do_something(*this)))
{
::do_something(*this);
}
};
However, gcc rejects this:
<source>:7:43: error: invalid use of 'this' at top level
noexcept(noexcept(::do_something(*this)))
If I just access a member, gcc is fine:
void do_something(int);
struct bar {
int x;
void fn()
noexcept(noexcept(::do_something(x)))
{
::do_something(x);
}
};
However, if I access the member through the this pointer, gcc complains again:
struct baz {
int x;
void fn()
noexcept(noexcept(::do_something(this->x)))
{
::do_something(this->x);
}
};
Diagnostic:
<source>:7:42: error: invalid use of 'this' at top level
noexcept(noexcept(::do_something(this->x)))
Every other compiler I tried accepts using this inside the noexcept specification, but I don't actually know if it's gcc that has the bug or all the other compilers.
Can the keyword this be used inside a noexcept specification?
Yes, it is allowed. [expr.prim.this]p2 says:
If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifier-seq and the end of the function-definition, [...].
The cv-qualifier-seq refers to the cv qualifiers of the member function, which appear before the noexcept specifier:
parameters-and-qualifiers:
( parameter-declaration-clause ) cv-qualifier-seq[opt] ref-qualifier[opt]
noexcept-specifier[opt] attribute-specifier-seq[opt]
So, this is a valid expression to use in the noexcept-specifier. This was a DR (cwg1207), which gcc doesn't implement. The bug report.

auto fails to deduce?

struct test
{
void f() {};
};
test t1;
using memfun_t = void (test::*)();
memfun_t mf = &test::f;
auto a1 = &test::f; // OK
auto a2 = t1.*mf; // error
auto a3 = &(t1.*mf); // still no luck
Any ideas why this can't be deduced? I would appreciate answers referencing the Standard.
Edit :
I have found a RAD Studio language extension called __closure that appears to be addressing this issue.1 Here is the code :
class base {
public:
void func(int x) {
};
};
typedef void(base::*pBaseMember)(int);
class derived : public base {
public:
void new_func(int i) {
};
};
int main(int argc, char * argv[]) {
derived derivedObject;
void(__closure * derivedClosure)(int);
// Get a pointer to the ‘new_func’ member.
// Note the closure is associated with the
// particular object, ‘derivedObject’.
derivedClosure = derivedObject.new_func;
derivedClosure(3); // Call ‘new_func’ through the closure.
return 0;
}
http://docwiki.embarcadero.com/RADStudio/Seattle/en/Closure
You can't use
auto a2 = t1.*mf; // error
just like you can't use:
auto a2 = t1.f;
t1.f is not a valid expression. A pointer to a member function cannot be obtained through an instance of the class. Unlike non-member functions, which decay to a function pointer when used like that, member functions don't decay to a member function pointer.
Relevant text from the C++11 Standard:
Unary Operators
...
4 A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [ Note: that is, the expression &(qualified-id), where the qualified-id is enclosed in parentheses, does not form an expression of type “pointer to member.” Neither does qualified-id, because there is no implicit conversion from a qualified-id for a non-static member function to the type “pointer to member function” as there is from an lvalue of function type to the type “pointer to function” (4.3). Nor is
&unqualified-id a pointer to member, even within the scope of the unqualified-id’s class. —end note ]

typeid doesn't work with non-static member function

clang doesn't compile the third call to typeid below (see live example). But I can't see anything in §5.2.8 that disallows this, specially when we consider that the expression B::f is not a glvalue of polymorphic class type (see paragraph 3). Also, according to this paragraph the expression B::f is an unevaluated operand, and as such, the call typeid(B::f) should compile. Note that GCC doesn't compile any of the calls to typeid below:
#include <iostream>
#include <typeinfo>
struct A{ int i; };
struct B{ int i; void f(); };
int main()
{
std::cout << typeid(A::i).name() << '\n';
std::cout << typeid(B::i).name() << '\n';
std::cout << typeid(B::f).name() << '\n';
}
As far as I can tell clang is correct, using a non static member is only valid in an unevaluated context if it is a data member. So it looks like gcc is incorrect for the first two cases, but gcc works correctly in the case of sizeof and decltype which also have unevaluated operands.
From the draft C++11 standard section 5.1.1 [expr.prim.general]:
An id-expression that denotes a non-static data member or non-static
member function of a class can only be used:
and includes the following bullet:
if that id-expression denotes a non-static data member and it appears
in an unevaluated operand. [ Example:
struct S {
int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK
—end example ]
The rest of the bullets do not apply, they are as follows:
as part of a class member access (5.2.5) in which the object expression refers to the member’s class61 or a class derived from that
class, or
to form a pointer to member (5.3.1), or
in a mem-initializer for a constructor for that class or for a class derived from that class (12.6.2), or
in a brace-or-equal-initializer for a non-static data member of that class or of a class derived from that class (12.6.2), or
We know that operand is unevaluated from section 5.2.8 which says:
When typeid is applied to an expression other than a glvalue of a
polymorphic class type, [...] The expression is an unevaluated operand
(Clause 5).
We can see from the grammar that an id-expression is either an unqualified-id or a qualified-id:
id-expression:
unqualified-id
qualified-id
Update
Filed a gcc bug report: typeid does not allow an id-expression that denotes a non-static data member.
typeid(A::i).name() doesn't quite do what I thought it would do. I expected it to be a pointer-to-member, but it's actually just an int.
To see this, run this code:
#include <iostream>
struct A{ int i; };
struct B{ int i; void f(void); };
template<typename T>
void what_is_my_type() {
std:: cout << __PRETTY_FUNCTION__ << std:: endl;
}
int main()
{
what_is_my_type<decltype(&A::i)>(); // "void what_is_my_type() [T = int A::*]"
what_is_my_type<decltype(&B::i)>(); // "void what_is_my_type() [T = int B::*]"
what_is_my_type<decltype(&B::f)>(); // "void what_is_my_type() [T = void (B::*)()]"
what_is_my_type<decltype(A::i)>(); // "void what_is_my_type() [T = int]"
what_is_my_type<decltype(B::i)>(); // "void what_is_my_type() [T = int]"
// what_is_my_type<decltype(B::f)>(); // doesn't compile
}
I've put the output in a comment after each call.
The first three calls work as expected - all three work and the type information includes the type of the struct (A or B) as well as the type of member.
The last three are different though. The final one doesn't even compile, and the first two simply print int. I think this is a clue as to what is wrong. It is possible, given a particular A or B, to take the address of that particular member:
A a;
int * x = &(a.i);
*x = 32;
but it is not possible (or even meaningful?) to do this:
B b;
??? y = &(a.f); // what does this even mean?
Finally, to emphasize that this is not about pointers, consider this:
A a;
B b;
int x = a.i;
int y = b.i;
??? z = b.f; // what would this mean? What's its type?

CV-qualified reference

8.3.2/1:
Cv-qualified references are ill-formed except when the cv-qualifiers
are introduced through the use of a typedef-name (7.1.3, 14.1) or
decltype-specificer (7.1.6.2), in which case the cv-qualifiers are
ignored
int a = 5;
const int &b = a;
int main()
{
}
Compiles fine by both gcc and clang. DEMO
Why? Is it a bug?
Example, provided by the Standard:
typedef int& A;
const A aref = 3; // ill-formed; lvalue reference to non-const initialized with rvalue
In your example you have no cv-qualified reference. The quote of the Standard means the following declarations that are ill formed
int a = 5;
const int & const b = a;
Here b is a cv-qualified reference and the code snippet is ill-formed.
As for your code snippet then it declares a reference to a const object.
This code snippet is ill formed
typedef int& A;
const A aref = 3;
because it is equivalent to
int & const aref = 3;
That it would be more clear compare the two declarations
int x;
int * const p = &x;
int & const r = x;
The declaration of the cv-qualified pointer is valid while the declaration of the cv-qualified reference is ill-formed.

Compile-time array constants

I seem to be missing something rather fundamental.
I'm trying to use const array members at compile-time.
const int list[3] = { 2, 5, 7 };
const int a = list[2]; // this doesn't error?
template<int N1, int N2>
struct tmax {
enum { value = ((N1 > N2) ? N1 : N2) };
};
const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // error is here
int main()
{
return 0;
}
Errors:
prog.cpp:10:24: error: 'list' cannot appear in a constant-expression
prog.cpp:10:30: error: an array reference cannot appear in a constant-expression
Here is the relevent IDEOne link
So why doesn't this work? What am I missing? What should I do differently?
Just because an object is const doesn't mean it's a compile time constant expression.
main.cpp:10:20: error: non-type template argument is not a constant expression
const int c = tmax<list[0],list[1]>::value; // error is here
^~~~~~~
main.cpp:10:20: note: read of non-constexpr variable 'list' is not allowed in a constant expression
main.cpp:1:11: note: declared here
const int list[3] = { 2, 5, 7 };
^
This is the reason for constexpr:
constexpr int list[3] = { 2, 5, 7 };
template<int N1, int N2>
struct tmax {
enum { value = ((N1 > N2) ? N1 : N2) };
};
const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // works fine now
As for why this works:
const int a = list[2]; // this doesn't error?
initializing a const variable doesn't require a constant expression:
int foo(int n) {
const int a = n; // initializing const var with a non-compile time constant
Expressions are not constant expressions if they contain any one of a number of disallowed sub-expressions. One such class of disallowed sub-expressions is:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding
initialization, initialized with a constant expression, or
a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers
to a sub-object of such an object, or
a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not
ended, initialized with a constant expression;
In particular, while the name of a const object of enum or intergral type initialized with a constant initializer forms a constant expression (reading its value is what causes the lvalue-to-rvalue conversion), sub-objects of an const aggregate object (such as list in your example, an array) do not, but would if declared constexpr.
const int list[3] = { 2, 5, 7 };
const int a = list[2];
This is valid but a does not constitute a constant expression because it is not initialized with a constant expression.
By changing the declaration of list (we don't have to change the declaration of a), we can make a form a constant expression.
constexpr int list[3] = { 2, 5, 7 };
const int a = list[2];
As list[2] is now a constant expression, a is now a const object of intergral type initialized with a constant expression so a can now be used as a constant expression.