This is probably a ridiculously easy question, but I've been searching around for the answer for a while yet can't seem to figure this out. I'm trying to initialize a constant variable constant pointer in a class. Here is the header file:
class Scheduler{
public:
Scheduler();
explicit Scheduler( unsigned long * );
private:
const unsigned long *const thresh;
};
And here is the constructor for the class
Scheduler::Scheduler( unsigned long * threshold ):
thresh(threshold)
{}
When I attempt to compile this code I run into this error:
scheduler.cpp: In constructor ‘Scheduler::Scheduler()’:
scheduler.cpp:3: error: uninitialized member ‘Scheduler::thresh’ with ‘const’ type ‘const long unsigned int* const’
Multiple sources online discussing constant member variables in constructors for member variables point to using initializer lists. I think I'm doing what I'm supposed to, but apparently it's still no good. Can anyone see what's wrong?
You must initialize your constant member in the initialization list of ALL constructors. You are doing it only for the one with an argument. Do it for the default one too, and everything will be fne. In this particular case, either initialize your thresh with 0, or disable the default constructor.
The problem is in the default constructor, it should be
Scheduler::Scheduler() : thresh(0) {}
or not be implemented at all.
Your code works for me (MSVC2010) - as I believe it should. What compiler are you trying this with?
The only complaint a compiler may/should have with the code is a warning that the automatic copy constructor and assignment operator cannot be created because of the const member.
Related
I am working on a module containing C and C++ codes. The problem is that I am getting the following forbidden warning. I provided the code that Is causing that warning.
warning: 'void* memset(void*, int, size_t)' clearing an object of type 'struct OtherStructure_s ' with no trivial copy-assignment; use assignment or value-initialization instead [-Wclass-memaccess]\n")
struct TEST {
explicit TEST();
OtherStructure_s _otherStructure;
};
TEST::TEST(){
memset(&_otherStructure, 0, sizeof(OtherStructure_s));
}
What is the best solution to remove that warning? If I initialize the structure in the constructor as if
TEST::TEST():_otherStructure(){} will that be a good solution?
OtherStructure_s doesn't have a trivial copy assignment operator. You can't use memset. Probably the class allocates some other resources like heap memory.
You don't need TEST::TEST():_otherStructure(){}. The default constructor of TEST will default construct _otherStructure. The best solution is to remove the constructor.
In C++, to declare an object of a class that has a member variable as const, we must have a user-defined default constructor. The following code illustrates this.
class Some {
int value;
};
int main() {
// error: default initialization of an object of const type 'const Some'
// without a user-provided default constructor
const Some some;
return 0;
}
However, if a member variable owned by a class is qualified as mutable, the compiler will not report any errors. For reference, I compiled using the command clang++ -std=c++17 -stdlib=libc++ helloworld.cpp -o helloworld.out --debug. I wonder if this result is due to a bug in the compiler or according to the syntax defined in the C++ language.
class Some {
mutable int value;
};
int main() {
const Some some;
return 0;
}
Rewriting my comment as an answer, hope it could helps someone.
It makes no sense declaring a const object if it is not initialized in some form.
Consider the following code:
const int x;
clang says: error: default initialization of an object of const type 'const int'.
gcc would say: error: uninitialized const ‘x’ [-fpermissive]
The logic behind this is that there is no sense in this type of declaration.
The value of x can never change, and therefore this code would be unpredictable as x would be mapped to uninitialized memory.
In your example, adding the keyword mutable to value means that although the Some instance is constant when declared as:
const Some some;
It is still possible to change value at a later time.
For example:
some.value = 8;
This means it is possible to use this code in a predictable manner, since value can be set later, and there are no uninitialized constants.
I stumbled across this when trying to clean up a temporary variable. It seemed interesting enough to discuss.
ArrayType m_ArrayOfThings;
INT32 m_BitfieldOfThings;
...
// Assume MyType has a ctor overload with argument ArrayType and another with int32.
const MyType my_obj( m_ArrayOfThings.IsEmpty() ? m_BitfieldOfThings : m_ArrayOfThings )
Here is a simplified example of my set-up. Something similar may be found in UE4's FCollisionObjectQueryParams constructors.
Compiling the above will result in error C2446: no conversion from 'INT32' to 'ArrayType', since the type passed is unknown at compile-time.
auto temp = m_ArrayOfThings.IsEmpty() ? m_BitfieldOfThings : m_ArrayOfThings;
const MyType my_obj( temp );
This will also throw: error C3536: 'temp': cannot be used before it is initialized.
It's obvious one should convert either variables to have matching types, so the type is known at compile-time, however, I was wondering if there exist semantics, tricks/hacks, or new features (c++17, etc.) to solve this as well.
You can just move constructor invocation to the right:
auto const my_obj{m_ArrayOfThings.IsEmpty() ? MyType{m_BitfieldOfThings} : MyType{m_ArrayOfThings}};
With C++17 this snippet will work even if MyType is non-copyable / non-movable and will invoke only one constructor.
I'm using codepad.org
class a {
private:
const unsigned long b = 100;
};
Line 3: error: ISO C++ forbids initialization of member 'b'
compilation terminated due to -Wfatal-errors.
Sorry if I've missed something obvious, but what could be wrong?
Before C++11, you can only initialise a static const data member in the class definition:
static const unsigned long b = 100;
This is usually what you want anyway. It doesn't make much sense to have all instances of a have this constant value duplicated among them.
If you enable C++11 support, your code will compile.
In-class initialization is only allowed for static const integral types in C++03, and your variable isn't static.
Either make it static (in this case, you should - note that you also have to define it in this casee), or use a C++11 compiler.
I've tried the following code snippet in 3 different compilers (G++, clang++, CL.exe) and they all report to me that they cannot disambiguate the overloaded constructors. Now, I know how I could modify the call to the constructor to make it pick one or the other (either make explicit that the second argument is a unsigned literal value or explicitly cast it).
However, I'm curious why the compiler would be attempting to choose between constructors in the first place given that one of the constructors is private and the call to the constructor is happening in the main function which should be outside the class's scope.
Can anyone enlighten me?
class Test
{
private:
Test(unsigned int a, unsigned int *b) { }
public:
Test(unsigned int a, unsigned int b) { }
};
int main()
{
Test t1 = Test(1,0); // compiler is confused
}
In C++, accessibility to class members doesn't influence the other language semantics. Instead, any invalid access causes a program to be ill-formed.
In other words, there is accessibility and visibility. The Standard has it straight
It should be noted that it is access to members and base classes that is controlled, not their visibility. Names of members are still visible, and implicit conversions to base classes are still considered, when those members and base classes are inaccessible. The interpretation of a given construct is established without regard to access control. If the interpretation established makes use of inaccessible member names or base classes, the construct is ill-formed.
The compiler does not attempt to select an overloaded function or constructor by its visibility.
It is more that the compiler will not refuse a candidate function even if it is marked as private. This means changing visability of a member will not changing existing code.
As for your second question, overload resolution happens before the test for visibility.
As for the first, you need to indicate to the compiler that the 0 is an unsigned int. As far as the compiler is concerned, the conversion from integer 0 to unsigned int is no better than the conversion from integer 0 to pointer.