26
votes
template <int* ip> struct test {};

struct q {
    static int a;
    int b;
    
    constexpr q(int b_) : b(b_) {}
};

int i;
constexpr q q0(2);

int main()
{
    constexpr test<&i> t1;      // Works fine
    constexpr test<&q::a> t2;   // Works 
    constexpr test<&q0.b> t3;   // Does not work; address of non-static member?
    
    return 0;
}

The declaration of t3 in the above piece of code fails despite the template argument &q0.b being known during compile time. Some googling revealed that this is disallowed by the standard (section 14.3.2):

[Note: Addresses of array elements and names or addresses of non-static class members are not acceptable template-arguments.

X<&s.m> x4; // error: address of non-static membe

So why exactly is this explicitly disallowed by the standard despite the addresses of non-static members of global variables being unique as well as known during compile-time?

2
I tried this on gcc 5.1.1 and clang 3.5.0 with -std=c++11nav
No (new q)->b is not known at compile timeuser2249683
It's type is not int*, it's int q::*, isn't it?skypjack
@ShafikYaghmour I deleted it because the question is more "why does this restriction exist" rather than "why does this code not compile?" Either way, it's disallowed in both wordings.Barry
Template arguments must be mangled, and mangling (arbitrarily complex) references to subobjects is a huge can of worms.T.C.

2 Answers

15
votes

First, to use pointers/references to subobjects, you'd need to be able to mangle them. That's a pretty big undertaking.

Second, and probably more importantly, from N4198:

The restriction that the constant expression must name a complete object is retained to avoid aliasing problems with pointers to subobjects:

struct A { int x, y; } a;
template<int*> struct Z;
using B = Z<&a.x + 1>;
using C = Z<&a.y>;
// Are B and C the same type?

To quote Richard Smith,

The answer "yes" is problematic because there are things you can do with a pointer to [a.y] that would have undefined behavior if performed on a pointer past the end of [a.x] The answer "no" is problematic because they (in typical implementations) represent the same address.

1
votes

Replace your main with this piece of code

int main(void)
{    
constexpr static int bb = 5;    
constexpr test<&bb> t;    
return 0;
}

and you will receive the error bb cannot be used as a template argument because it has no linkage ( not to be mistaken with linker related staff ) .

Class data members cannot be accessed unless they are referenced through the object, and cannot be taken into account during template instantiation, since data members do not have linkage, i.e. they are not defined symbols and therefore cannot be used as template arguments.

That being said , doing a readelf you can verify this:

48: 00000000004006ac     4 OBJECT  LOCAL  DEFAULT   14 q0


68: 000000000060097c     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
69: 0000000000600978     4 OBJECT  GLOBAL DEFAULT   23 q::a

but there is no q0.b defined . The alternative would be to name (mangle) none static class members which would strip dynamic capabilities of the language.