Reputation: 655
It does not seem to be possible to pass a reference to the base class object of a derived object as a template parameter, as I try to do here:
struct a
{ int _v;
constexpr a():_v(0){}
constexpr a(int v): _v(v){}
};
struct c: public a
{ constexpr c():a(){}
constexpr c(int v):a(v){}
};
extern const c default_a;
constexpr const c default_a { 1 };
const a& c_as_a = default_a;
// ^-- this line (16) causes no error - c can be converted to a
template < const a & The_A = default_a >
struct b
{ constexpr static const a &the_a = The_A;
};
b<> a_b;
// Template instantiation causes error:
// t.C:24:7: error: could not convert template argument 'default_a' to 'const a&'
// b<> a_b;
// ^
I would have expected the 'c' object 'default_a', since it is derived from 'a', to be acceptable as a 'const a&', as it is on the line 16.
Why isn't this OK as a template parameter ?
What section of specification actually mandates this behaviour ?
Maybe my build of gcc-5.3.0 is somehow defective ?
Anyone found a good workaround / way of passing a derived object as a base class object reference template parameter ?
I cannot just substitute the reference variable 'c_as_a' for 'default_a' in template argument list:
template < const a & The_A = c_as_a >
t.C:24:7: error: 'const a& c_as_a' is not a valid template argument for type 'const a&' because a reference variable does not have a constant address b<> a_b;
Nor can I substitute any constexpr function call which does something like:
constexpr const a& c_as_a( const c &c ){ return *reinterpret_cast<const a*>(&c);}
...
template < const a & The_A = c_as_a( default_a ) >
since this call is not an 'object with external linkage'.
Any suggestions how to achieve passing a reference to a base class of a derived object as a template parameter would be much appreciated - it's got to be possible, I just can't see how ATM.
There must be a way of specifying a reference to an object's base class object as a template parameter.
Upvotes: 6
Views: 716
Reputation: 655
Fix that works in namespace:
namespace U {
struct a
{ int _v;
constexpr a():_v(0){}
constexpr a(int v): _v(v){}
};
struct c: public a
{ constexpr c():a(){}
constexpr c(int v):a(v){}
};
extern "C" {
extern const c _default_c;
constexpr const c _default_c { 1 };
}
extern const a default_a;
const a default_a __attribute__((alias("_default_c")));
template < const a & The_A = default_a >
struct b
{ constexpr static const a &the_a = The_A;
};
b<> a_b;
}
Upvotes: 0
Reputation: 19
The problem is when initializing a const
reference a temporary is created, and temporaries are not allowed with reference initialization in this context (converted constant expression).
N4140 8.5.3 (5.2.2.1)
...
- If
T1
is a non-class type, a temporary of type "cv1T1
" is created and copy-initialized (8.5) from the initializer expression. The reference is then bound to the temporary.In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.
Then in 5.19:
A conditional-expression
e
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:(2.9) - an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
- it is initialized with a constant expression or ...
Upvotes: 0
Reputation: 655
A gcc specific workaround:
struct a
{ int _v;
constexpr a():_v(0){}
constexpr a(int v): _v(v){}
};
struct c: public a
{ constexpr c():a(){}
constexpr c(int v):a(v){}
};
extern const c _default_c;
constexpr const c _default_c { 1 };
extern const a default_a;
const a default_a __attribute__((alias("_default_c")));
template < const a & The_A = default_a >
struct b
{ constexpr static const a &the_a = The_A;
};
b<> a_b;
The above compiles OK .
Happily, we know that the name of '_default_c' is not mangled.
Upvotes: 1