C++11 decltype of member - c++

Why can't I do this:
class Foo {
void fn();
using fn_t = decltype(fn); //call to non-static member function without an object argument
};
But I can do
class Foo {
static void fn();
using fn_t = decltype(fn);
};
This SO post claims:
Within unevaluated operands (operands of decltype, sizeof, noexcept, ...) you can name nonstatic data members also outside of member functions

fn is a valid id-expression denoting a non-static member function. §5.1.1 [expr.prim.general]/p13 (footnote omitted):
An id-expression that denotes a non-static data member or non-static
member function of a class can only be used:
as part of a class member access (5.2.5) in which the object expression refers to the member’s class or a class derived from that
class, or
to form a pointer to member (5.3.1), or
if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
§7.1.6.2 [dcl.type.simple]/p4:
The operand of the decltype specifier is an unevaluated operand
(Clause 5).
Since decltype is not one of the few contexts in which an id-expression denoting a non-static member function may be used, the program is ill-formed.

Related

Is decltype of a non-static member function ill-formed?

I'm not sure to perfectly understand [dcl.type]/4.3:
For an expression e, the type denoted by decltype(e) is defined as follows:
[...]
(4.3) otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
[...]
For me, the emphasized part both apply to id-expression and class member access, right?
Playing with my favorite compiler, I get the following.
✓ Accepted by the compiler
namespace N { void f() {} }
using type = decltype(N::f);
type* pf = N::f;
Ok I guess; N::f is an unparenthesized id-expression and does not name a set of overloaded functions.
✗ Rejected by the compiler
namespace N { void f() {} void f(int) {} }
using type = decltype(N::f); // error: decltype cannot resolve address of overloaded function
type* pf = N::f;
Ok; N::f does name a set of overloaded functions.
✗ Rejected by the compiler
struct S { void f(){} };
using type = decltype(S::f); // error: invalid use of non-static member function 'void S::f()'
type* pf = &S::f;
Hum? S::f would name a set of one overloaded function?
All in all, is my understanding of [dcl.type]/4.3 just bad? is gcc trunk wrong? both? none? kamoulox?
The simple reason is that the use of S::f is constrained for class members.
[expr.prim.id]
2 An id-expression that denotes a non-static data member or
non-static member function of a class can only be used:
as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or
to form a pointer to member ([expr.unary.op]), or
if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
The last bullet, the one related to your code, only applies to non-static data member. There is no provision for functions.
I can only speculate as to why it's not allowed, though I previously asked that question.
It's worth a note that decltype(&S::f) works here as pointer-to-member-function type,
again unless the f names a set of overloaded (member) functions.
The function type itself can be extracted from the pointer-to-member-function type.
If the member function is cv-or-ref qualified then it has an abominable function type.
The std traits are lacking here - a library like Boost.CallableTraits helps.

Is this function pointer with `this` in trailing return type legal?

class C {
auto (*foo)() -> decltype(this);
};
This code is accepted by GCC, MSVC, and clang, but not icc.
Quoting n4140 (roughly C++14) [expr.prim.general]:
3 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-qualifer-seq and the end of the function-definition, member-declarator, or declarator. It shall not appear before the optional cv-qualifier-seq and it shall not appear within the declaration of a static member function (although its type and value category are defined within a static member function as they are within a non-static member function). [...]
4 Otherwise, if a member-declarator declares a non-static data member (9.2) of a class X, the expression this is a prvalue of type "pointer to X" within the optional brace-or-equal-initializer. It shall not appear elsewhere in the member-declarator.
Since you're not declaring a member function or member function template, p3 doesn't apply, but this is what would make the code valid for the non-pointer case where you actually declare a member function: the trailing return type is between the optional cv-qualifier-seq and the end of the declarator, as made clearer in the definition of a const member function:
auto foo() const -> decltype(this) { }
p4 is what applies here though. It allows this only to appear in the initialiser. You're putting it elsewhere. p3 doesn't apply, so ICC is correct to reject this.

Type of the first parameter of a member function in C++11

I have written a metafunction to retrieve the type of the first parameter of a member function, which of course receives one or more parameters. The code I have written is as follow:
template <typename...> struct parameter;
template < typename O, typename A, typename R, typename... Args>
struct parameter <R (O::*)(A, Args...) > {
using first_param = A;
};
I use this meta function as follow:
using mem_fn = void(mem_type::*)(std::vector<int>);
using f_pm = parameter<mem_fn>::first_param;
and it compiles and works. But when I have a class:
struct mem_type{
void update(std::vector<int>) {
}
};
and use my metafunction as follow:
using mem_fn = decltype(mem_type::update);
using f_pm = parameter<mem_fn>::first_param;
the code does not compiles and visual studio 2013 gives: error C2027: use of undefined type parameter<mem_fn>.
Does anyone knows the reason for this error?
First, an id-expression naming a nonstatic member function (mem_type::update in this case) can't be used as an unevaluated operand (such as the operand of decltype). §5.1.1 [expr.prim.general]/p13 (footnote omitted):
An id-expression that denotes a non-static data member or non-static
member function of a class can only be used:
as part of a class member access (5.2.5) in which the object expression refers to the member’s class or a class derived from that
class, or
to form a pointer to member (5.3.1), or
if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
§7.1.6.2 [dcl.type.simple]/p4:
The operand of the decltype specifier is an unevaluated operand
(Clause 5).
And even if update were a regular function, decltype would produce a function type rather than a function pointer type, and your specialization matches a pointer-to-member-function type.
You need to created a pointer-to-member-function with & - i.e., decltype(&mem_type::update).

Class member access inside the member function body

The following simple code example causes some doubts for me:
#include <iostream>
using namespace std;
struct A
{
int a;
A(int a)
{
A::a = a; //It is unclear, because in that case we're applying
//scope resolution operator to A and does.
this -> a = a; //It is clear, because this points to the current object.
}
};
int main()
{
A a(4);
cout << a.a;
}
demo
I know that the section 3.4.3.1/3 says:
A class member name hidden by a name in a nested declarative region or
by the name of a derived class member can still be found if qualified
by the name of its class followed by the :: operator.
But it doesn't specify that the name looked up with "Qualified name lookup" (e.g. A::a in my case) inside the member function shall denote a member of current object.
I'm looking for relevant reference in the Standard.
When searching for something specific to nonstatic class member functions, you should look first at the subclause governing...nonstatic class member functions. §9.3.1 [class.mfct.non-static]/p3:
When an id-expression (5.1) that is not part of a class member access
syntax (5.2.5) and not used to form a pointer to member (5.3.1) is
used in a member of class X in a context where this can be used
(5.1.1), if name lookup (3.4) resolves the name in the id-expression
to a non-static non-type member of some class C, and if either the
id-expression is potentially evaluated or C is X or a base class of X,
the id-expression is transformed into a class member access expression
(5.2.5) using (*this) (9.3.2) as the postfix-expression to the left of
the . operator. [ Note: If C is not X or a base class of X, the class
member access expression is ill-formed. —end note ] Similarly during
name lookup, when an unqualified-id (5.1) used in the definition of a
member function for class X resolves to a static member, an enumerator
or a nested type of class X or of a base class of X, the
unqualified-id is transformed into a qualified-id (5.1) in which the
nested-name-specifier names the class of the member function.
C++.11 §5.1.1¶8
A nested-name-specifier that denotes a class, optionally followed by the keyword template (14.2), and then followed by the name of a member of either that class (9.2) or one of its base classes (Clause 10), is a qualified-id; 3.4.3.1 describes name lookup for class members that appear in qualified-ids. The result is the member. The type of the result is the type of the member. The result is an lvalue if the member is a static member function or a data member and a prvalue otherwise.

What is the type of decltype(this) in C++?

Apparently clang thinks decltype(this) is a pointer to the cv-qualified class, while gcc thinks it is a const reference to a pointer to the cv-qualified class. GCC only thinks decltype(&*this) is a pointer to the cv-qualified class. This has some implications when it is used as the typename for a template. Consider a hypothetical example:
template<typename T>
class MyContainer {
/* ... */
template<typename ContainerPtr>
class MyIterator {
ContainerPtr container;
/* ... */
};
auto cbegin() const
-> MyIterator<decltype(&*this)> { return { /* ... */ }; }
auto cend() const
-> MyIterator<decltype(this)> { return { /* ... */ }; }
};
In this example, one implements a custom container of T. Being a container, it supports iterators. In fact, two kinds of iterators: iterators and const_iterators. It would not make sense to duplicate the code for these two, so one could write a template iterator class, taking either a pointer to the original class MyContainer<T> * or a pointer to the const version MyContainer<T> const *.
When cbegin and cend are used together, gcc errors out, saying it deduced conflicting types, while clang just works fine.
this is a prvalue, so decltype(this) should always be plain X* (or X cv* / cv X*). The addition of const& seems to be a bug in GCC (tested with g++ 4.8.1), which happens only for a class template (not for a "plain" class) and only inside the trailing return type (not inside the body of the member function): demo. This seems to be fixed in GCC 4.9 (experimental), you can test here.
Okay, here is what I found in the standard (N3337) though:
7.1.6.2 Simple type specifiers [dcl.type.simple]
4 The type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression or an
unparenthesized class member access (5.2.5), decltype(e) is the type
of the entity named by e. If there is no such entity, or if e
names a set of overloaded functions, the program is ill-formed;
— otherwise, if e is an xvalue, decltype(e) is T&&, where
T is the type of e; — otherwise, if e is an lvalue,
decltype(e) is T&, where T is the type of e; —
otherwise, decltype(e) is the type of e. The operand of the
decltype specifier is an unevaluated operand (Clause 5).
and
5.1.1 General [expr.prim.general]
3 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-qualifer-seq
and the end of the function-definition, member-declarator, or
declarator. It shall not appear before the optional cv-qualifier-seq
and it shall not appear within the declaration of a static member
function (although its type and value category are defined within a
static member function as they are within a non-static member
function). [ Note: this is because declaration matching does not occur
until the complete declarator is known. — end note ] Unlike the object
expression in other contexts, *this is not required to be of complete
type for purposes of class member access (5.2.5) outside the member
function body. [ Note: only class members declared prior to the
declaration are visible. — end note ]
The previous reference to §9.3.2 is an error, since that deals with the body of a member function, as pointed out below in a comment by MWid.
9.3.2 The `this` pointer [class.this]
1 In the body of a non-static (9.3) member function, the
keyword `this` is a prvalue expression whose value is the address of
the object for which the function is called. The type of `this` in a
member function of a class `X` is `X*`. If the member function is
declared `const`, the type of `this` is `const X*`, if the member
function is declared `volatile`, the type of `this` is `volatile X*`,
and if the member function is declared `const volatile`, the type of
`this` is `const volatile X*`.
So it looks like gcc is wrong.