JVD
JVD

Reputation: 655

non-type template parameter : how to pass reference to base class object?

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

Answers (3)

JVD
JVD

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

user6320913
user6320913

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 "cv1 T1" 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 of e, 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

JVD
JVD

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

Related Questions