This question already has an answer here:
decltype in class method declaration: error when used before "referenced" member is declared
(1 answer)
Closed 8 years ago.
My compiler is GCC 4.9.0.
struct A {
int n;
auto f() -> decltype(n) { // OK
return n;
}
};
struct B {
auto f() -> decltype(n) { // error: 'n' was not declared in this scope
return n;
}
int n;
};
int main() {
return A().f() + B().f();
}
Why does the order of the class members matter?
The declarations are compiled in order. It's the same reason you can't write:
int y = x;
int x = 5;
The bodies of inline functions (incl. c-tor initiailizer lists) are processed later (parsed first of course, but the names aren't looked up until after the class definition is complete) so they can refer to class members that are on later lines.
Related
AFAIK, data member of enclosing class are also visible in nested class.
struct A {
struct B {
int arr[n]; // how come n is not visible here, but it is visible in next statement.
int x = n;
};
static const int n = 5;
};
see live demo here
how come n is not visible here, but it is visible in next statement.
Because int x = n; is a complete class context while int arr[n]; is not. This can be understood from class.mem which states:
6) A complete-class context of a class is a:
6.4) default member initializer
within the member-specification of the class.
[ Note: A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested class is defined within the member-specification of the enclosing class.
— end note
]
7) A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.
(emphasis mine)
Note that the n inside int arr[n]; is part of the type of the array but the above quoted statement doesn't allow n to be used as part of a type of a non-static data member and hence the error. For the same reason, we will get the same error if we wrote:
struct A {
struct B {
int x = n;
//----------------------v--------->same error as before
std::array<int, n> arr;
};
static constexpr int n = 5;
};
My answer is more like intuition of why this is so:
In int arr[n] n is used in definition of a filed, while in int x = n n is used to initialize a field. It is like B() : x(n) {}.
Think of the following example:
struct A {
struct B {
int arr[n];
int x = n;
};
static constexpr int n = offsetof(B, x);
};
Here the value of n is dependent on the size of arr and size of the arr is dependent on n. It is a cross dependency. Therefore the struct is not well defined.
If the rules of the C++ was so that your use case be legal, then this should also be legal, which can't be. However, it still allow us to use offsetof in initialization, because why not?
Something about evaluation variable in left-hand side of assignment, not a computer language expert, the following worked.
#include <iostream>
using namespace std;
struct A {
struct B {
int *arr = new int[n]();
int x = n;
};
static const int n = 5;
};
int main() {
cout << "Hello World!";
return 0;
}
Is there a nice way to have a non static value as default argument in a function? I've seen some older responses to the same question which always end up in explicitly writing out the overload. Is this still necessary in C++17?
What I'd like to do is do something akin to
class C {
const int N; //Initialized in constructor
void foo(int x = this->N){
//do something
}
}
instead of having to write
class C {
const int N; //Initialized in constructor
void foo(){
foo(N);
}
void foo(int x){
//do something
}
}
which makes the purpose of the overload less obvious.
One relatively elegant way (in my opinion) would be to use std::optional to accept the argument, and if no argument was provided, use the default from the object:
class C {
const int N_; // Initialized in constructor
public:
C(int x) :N_(x) {}
void foo(std::optional<int> x = std::nullopt) {
std::cout << x.value_or(N_) << std::endl;
}
};
int main() {
C c(7);
c.foo();
c.foo(0);
}
You can find the full explanation of what works/doesn't work in section 11.3.6 of the standard. Subsection 9 describes member access (excerpt):
A non-static member shall not appear in a default argument unless it
appears as the id-expressionof a class member access expression
(8.5.1.5) or unless it is used to form a pointer to member
(8.5.2.1).[Example:The declaration of X::mem1()in the following example
is ill-formed because no object is supplied for the non-static
memberX::a used as an initializer.
int b;
class X {
int a;
int mem1(int i = a);// error: non-static memberaused as default argument
int mem2(int i = b);// OK; useX::b
static int b;
};
This question already has an answer here:
Why can in-class initializers only use = or {}? [duplicate]
(1 answer)
Closed 5 years ago.
This is the example from Effective modern c++.
class Widget {
…
private:
int x{ 0 }; // fine, x's default value is 0
int y = 0; // also fine
int z(0); // error!
};
direct initialization using ()
Inside the class treats the below
int z(0);
As a function as and expects parameter .As result error
Expected parameter declarator
alternatively can be done
class Widget {
private:
int x{ 0 };
int y = 0;
int z;
public:
Widget():z(0){}
};
This question already has answers here:
decltype as a return type in class member function
(3 answers)
Closed 5 years ago.
For example
struct A
{
auto count() -> decltype(m_count) { return m_count; }
int m_count;
};
The above gets compilation error because m_count in decltype is not recognized. How to work around it? auto return and get the type from m_count must be used.
The code compiles when the order is changed
struct A
{
int m_count;
auto count() -> decltype(m_count) { return m_count; }
};
but how do I get the first case to work?
The trailing return type is part of the member function declaration, and not the member function definition ([dcl.fct]/2). That's the reason why you can use m_count within the function body even when the data member follows the member function definition.
However, when used in a declaration, the name in question must be declared before its use.
§3.4.1/7 [basic.lookup.unqual]
A name used in the definition of a class X outside of a member function body or nested class definition shall be declared in one of the following ways:
— before its use in class X or be a member of a base class of X (10.2), or
— ...
In your case, you need to place the declaration of m_count ahead of count(); or if you have access to a C++14 compiler, you can omit the trailing return type altogether.
struct A
{
auto count() { return m_count; } // OK in C++14
int m_count;
};
In C++, you can't use a name that hasn't been introduced (declared) in a declaration, including in a decltype for a trailing return type. So you must reorder your declarations :
struct A
{
int m_count;
auto count() -> decltype(m_count) { return m_count; }
};
There is this code:
namespace N {
struct B {
void f() {
i;
j;
}
int i;
};
int j;
}
int main() {
return 0;
}
Variable i is found but variable j is not. How does it work that variable in the class can be used before it is declared but the same does not work for namespace? How i is found - compiler parses first all class to find all members then bind it with references from member functions?
Indeed, the bodies of member functions (as well as variable initializers) are processed in a later phase than the class definition. You can check for yourself that the "declare before use" rule is still in full effect for class members, by trying to use members in other member declarations:
struct B
{
char c[sizeof i];
int i;
};
Demonstration: http://ideone.com/v1ksio
struct B2
{
decltype(i) f();
int i;
};
This also affects usage of the class itself which requires a complete type.
struct B
{
static char c[sizeof (B)];
};
Demonstration: http://ideone.com/Z9XOzm
But this is ok, because variable initializers are processed when the construction definitions are, where all members are known and the type is complete:
struct B
{
unsigned r{sizeof s};
unsigned s{sizeof (B)};
};