Reputation:
I have a Singleton class here designed to be inherited by classes wanting to use it. But I have a question about something the compiler is allowing and I don't understand why:
template<class DerivedT>
class Singleton {
static DerivedT*& internalPointer() {
static DerivedT* object(nullptr);
return object;
}
void reassignIf(Singleton* instance) {
auto& object(internalPointer());
if (object == static_cast<DerivedT*>(instance)) {
object = static_cast<DerivedT*>(this);
}
}
Singleton(Singleton const& other); // = delete
Singleton& operator=(Singleton const& other); // = delete
public:
static DerivedT* instance() { return internalPointer(); }
protected:
Singleton() { reassignIf(nullptr); }
Singleton(Singleton&& other) { reassignIf(&other); }
~Singleton() {
auto& object(internalPointer());
if (object == static_cast<DerivedT*>(this)) {
object = nullptr;
}
}
Singleton& operator=(Singleton&& other) { reassignIf(&other); return *this; }
};
class SingletonMock: Singleton<SingletonMock> {
public:
using Singleton<SingletonMock>::instance;
SingletonMock() : Singleton<SingletonMock>() { }
};
int main() {
SingletonMock x;
SingletonMock const y; // why is this line allowed?
}
Why is the bottom line allowed? The Singleton base class has a static member function called internalPointer that has a static DerivedT*. This should become SingletonMock*, but it's allowing a const SingletonMock to be assigned to that variable.
EDIT:
I think I understand why it's working now from the comments. However, specifically it's allowing me to do this:
SingletonMock const y;
SingletonMock* yPtr(SingletonMock::instance());
How can I stop something like that from being allowed?
Upvotes: 2
Views: 408
Reputation: 393769
Why would it not be allowed? The singleton seems irrelevant to your question.
Edit Now I see. This is what g++-4.6 has to say about this pearl:
g++-4.6 --std=c++0x -O2 t.cpp -o t
t.cpp: In function ‘int main()’:
t.cpp:65:25: error: uninitialized const ‘y’ [-fpermissive]
t.cpp:56:7: note: ‘const class SingletonMock’ has no user-provided default constructor
t.cpp: In destructor ‘Singleton<DerivedT>::~Singleton() [with DerivedT = SingletonMock]’:
t.cpp:56:7: instantiated from here
t.cpp:43:9: error: ‘Singleton<SingletonMock>’ is an inaccessible base of ‘SingletonMock’
t.cpp: In member function ‘void Singleton<DerivedT>::reassignIf(Singleton<DerivedT>*) [with DerivedT = SingletonMock, Singleton<DerivedT> = Singleton<SingletonMock>]’:
t.cpp:32:9: instantiated from ‘Singleton<DerivedT>::Singleton() [with DerivedT = SingletonMock]’
t.cpp:56:7: instantiated from here
t.cpp:13:9: error: ‘Singleton<SingletonMock>’ is an inaccessible base of ‘SingletonMock’
t.cpp:15:13: error: ‘Singleton<SingletonMock>’ is an inaccessible base of ‘SingletonMock’
It does require some prodding to acept this code, which is indeed not standard comformant.
Original, inane answer, just for fun :)
It's just allowed ordering of a declaration specifier:
int main()
{
int x = 1;
const int y = 2;
int const z = 3;
volatile const int a = 2;
int volatile const b = 3;
int const volatile c = 3;
return z;
}
For fun, see this example on codepad.org: _these are all exactly identical variable declarations (aside from the name and initializer, obviously):
int main()
{
volatile register const unsigned int v1 = 1;
volatile register const int unsigned v2 = 2;
volatile register unsigned const int v3 = 3;
volatile register unsigned int const v4 = 4;
volatile register int const unsigned v5 = 5;
volatile register int unsigned const v6 = 6;
volatile const register unsigned int v7 = 7;
volatile const register int unsigned v8 = 8;
volatile const unsigned register int v9 = 9;
volatile const unsigned int register v10 = 10;
volatile const int register unsigned v11 = 11;
volatile const int unsigned register v12 = 12;
volatile unsigned register const int v13 = 13;
volatile unsigned register int const v14 = 14;
volatile unsigned const register int v15 = 15;
volatile unsigned const int register v16 = 16;
volatile unsigned int register const v17 = 17;
volatile unsigned int const register v18 = 18;
volatile int register const unsigned v19 = 19;
volatile int register unsigned const v20 = 20;
volatile int const register unsigned v21 = 21;
volatile int const unsigned register v22 = 22;
volatile int unsigned register const v23 = 23;
volatile int unsigned const register v24 = 24;
register volatile const unsigned int v25 = 25;
register volatile const int unsigned v26 = 26;
register volatile unsigned const int v27 = 27;
register volatile unsigned int const v28 = 28;
register volatile int const unsigned v29 = 29;
register volatile int unsigned const v30 = 30;
register const volatile unsigned int v31 = 31;
register const volatile int unsigned v32 = 32;
register const unsigned volatile int v33 = 33;
register const unsigned int volatile v34 = 34;
register const int volatile unsigned v35 = 35;
register const int unsigned volatile v36 = 36;
register unsigned volatile const int v37 = 37;
register unsigned volatile int const v38 = 38;
register unsigned const volatile int v39 = 39;
register unsigned const int volatile v40 = 40;
register unsigned int volatile const v41 = 41;
register unsigned int const volatile v42 = 42;
register int volatile const unsigned v43 = 43;
register int volatile unsigned const v44 = 44;
register int const volatile unsigned v45 = 45;
register int const unsigned volatile v46 = 46;
register int unsigned volatile const v47 = 47;
register int unsigned const volatile v48 = 48;
const volatile register unsigned int v49 = 49;
const volatile register int unsigned v50 = 50;
const volatile unsigned register int v51 = 51;
const volatile unsigned int register v52 = 52;
const volatile int register unsigned v53 = 53;
const volatile int unsigned register v54 = 54;
const register volatile unsigned int v55 = 55;
const register volatile int unsigned v56 = 56;
const register unsigned volatile int v57 = 57;
const register unsigned int volatile v58 = 58;
const register int volatile unsigned v59 = 59;
const register int unsigned volatile v60 = 60;
const unsigned volatile register int v61 = 61;
const unsigned volatile int register v62 = 62;
const unsigned register volatile int v63 = 63;
const unsigned register int volatile v64 = 64;
const unsigned int volatile register v65 = 65;
const unsigned int register volatile v66 = 66;
const int volatile register unsigned v67 = 67;
const int volatile unsigned register v68 = 68;
const int register volatile unsigned v69 = 69;
const int register unsigned volatile v70 = 70;
const int unsigned volatile register v71 = 71;
const int unsigned register volatile v72 = 72;
unsigned volatile register const int v73 = 73;
unsigned volatile register int const v74 = 74;
unsigned volatile const register int v75 = 75;
unsigned volatile const int register v76 = 76;
unsigned volatile int register const v77 = 77;
unsigned volatile int const register v78 = 78;
unsigned register volatile const int v79 = 79;
unsigned register volatile int const v80 = 80;
unsigned register const volatile int v81 = 81;
unsigned register const int volatile v82 = 82;
unsigned register int volatile const v83 = 83;
unsigned register int const volatile v84 = 84;
unsigned const volatile register int v85 = 85;
unsigned const volatile int register v86 = 86;
unsigned const register volatile int v87 = 87;
unsigned const register int volatile v88 = 88;
unsigned const int volatile register v89 = 89;
unsigned const int register volatile v90 = 90;
unsigned int volatile register const v91 = 91;
unsigned int volatile const register v92 = 92;
unsigned int register volatile const v93 = 93;
unsigned int register const volatile v94 = 94;
unsigned int const volatile register v95 = 95;
unsigned int const register volatile v96 = 96;
int volatile register const unsigned v97 = 97;
int volatile register unsigned const v98 = 98;
int volatile const register unsigned v99 = 99;
int volatile const unsigned register v100 = 100;
int volatile unsigned register const v101 = 101;
int volatile unsigned const register v102 = 102;
int register volatile const unsigned v103 = 103;
int register volatile unsigned const v104 = 104;
int register const volatile unsigned v105 = 105;
int register const unsigned volatile v106 = 106;
int register unsigned volatile const v107 = 107;
int register unsigned const volatile v108 = 108;
int const volatile register unsigned v109 = 109;
int const volatile unsigned register v110 = 110;
int const register volatile unsigned v111 = 111;
int const register unsigned volatile v112 = 112;
int const unsigned volatile register v113 = 113;
int const unsigned register volatile v114 = 114;
int unsigned volatile register const v115 = 115;
int unsigned volatile const register v116 = 116;
int unsigned register volatile const v117 = 117;
int unsigned register const volatile v118 = 118;
int unsigned const volatile register v119 = 119;
int unsigned const register volatile v120 = 120;
return 0;
}
All valid C++
Upvotes: 3
Reputation: 507283
That line is not allowed by C++. The spec says (8.5p9):
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor.
(emphasize mine). C++0x (FDIS) wording (8.5p6 and 8.5p11):
If no initializer is specified for an object, the object is default-initialized;
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
Upvotes: 6
Reputation: 54634
I think the question is: Why does the compiler allow me to declare a const Singleton
? I'm assuming you are wondering how the compiler can let you create a const Singleton
when you only want to have pointers to a non-const Singleton
. If you declare a const instance of a class that has a constructor, it still needs to be constructed. In that constructor, it is not const. Once the constructor finishes, it can only be accessed as const, but that doesn't apply while it's being constructed. Otherwise, you couldn't have const instances of any class.
Upvotes: 0