Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ nontype template argument taking inherited class

I want to be able to pass references of objects inherited from DBMetaData as a nontype template argument of another class, DBVar:

#include    <iostream>

class   DBMetaData
{
public:
    virtual const char  *description( ) const   = 0;
};

class   DBMetaData_NT
:   public  DBMetaData
{
public:
    const char  *description( ) const
    {   return  "Useless description."; }
};

#if DO_WHAT_I_WANT
template< const DBMetaData &Metadata >
#else
template< typename MetadataType,
    const MetadataType &Metadata >
#endif  // DO_WHAT_I_WANT
class DBVar
{
public:
    /// Descrição da variavel.
    const char  *description( ) const
    {   return  Metadata.description( ); }
};

DBMetaData_NT       _md_u1;

#if DO_WHAT_I_WANT
DBVar< _md_u1 > _u1;
#else
DBVar< DBMetaData_NT, _md_u1 >  _u1;
#endif  // DO_WHAT_I_WANT

int main( )
{
    std::cout << "_md_u1.description( ) = " << _md_u1.description( ) << std::endl;
    std::cout << "_u1.description( ) = "    << _u1.description( ) << std::endl;

    return  0;
}

I can compile and run the above example but I need to explicitly specify teh inherited type.

If I try to compile it defining DO_WHAT_I_WANT (I want to pass a reference - or pointer - of type DBMetaData to an object of any inherited class), I get the error:

templ_inh_arg.cpp:36:15: error: could not convert template argument ‘_md_u1’ to ‘const DBMetaData&’
templ_inh_arg.cpp:36:20: error: invalid type in declaration before ‘;’ token

Why can't I pass _u1, that is of type DBMetaData_NT that inherits from DBMetaData as parameter for DBVar< _md_u1 > _u1;?

Is there any way to get what I want?

Thanks!


EDIT:

Replacing the template parameter with a function pointer, as suggested by @ecatmur solved my problem and, I must note, turned my code a little more readable.

#include    <iostream>

class   DBMetaData
{
public:
    /// Descrição da variavel.
    virtual const char  *description( ) const   = 0;
};

class   DBMetaData_NT
:   public  DBMetaData
{
public:
    const char  *description( ) const
    {   return  "Useless description."; }
};


typedef     const DBMetaData &( *metadata )( );

template< metadata Metadata >
class DBVar
{
public:
    /// Descrição da variavel.
    const char  *description( ) const
    {   return  Metadata( ).description( ); }
};

const DBMetaData & _md_u1_metadata( )
{
    static const DBMetaData_NT      _md_u1;

    return  _md_u1;
}

DBVar< _md_u1_metadata >    _u1;

int main( )
{
    std::cout << "_md_u1_metadata( ).description( ) = " << _md_u1_metadata( ).description( ) << std::endl;
    std::cout << "_u1.description( ) = "    << _u1.description( ) << std::endl;

    return  0;
}
like image 505
j4x Avatar asked Nov 28 '12 13:11

j4x


2 Answers

Unfortunately not. Per 14.3.2 Template non-type arguments, paragraph 1:

A template-argument for a non-type, non-template template-parameter shall be one of: [...]

  • a constant expression that designates the address of an object with static storage duration [...], expressed (ignoring parentheses) as & id-expression, except that the & [...] shall be omitted if the corresponding template-parameter is a reference.

Derived-to-base conversions are not permitted, per paragraph 5 of the same section:

  • For a non-type template-parameter of type reference to object, no conversions apply. The type referred to by the reference may be more cv-qualified than the (otherwise identical) type of the template-argument. The template-parameter is bound directly to the template-argument, which shall be an lvalue.

This also means that a cast is not permitted, as this is not of the form [&] id-expression, and does not yield an lvalue.

Depending on what you're trying to accomplish, you may be able to achieve a similar result through manually simulating polymorphism e.g. having _md_u1 initialised as the return value of a function which sets up an appropriate vtable pointer or table.

like image 177
ecatmur Avatar answered Sep 30 '22 00:09

ecatmur


You can do it with C++11's decltype and a macro like following:

#define DBVAR(metadata) DBVar<decltype(metadata), metadata>

Now, to define a variable:

DBVAR(_md_u1) _u1;

Hope this helps.

EDIT: To be honest I don't like your approach. I'd rather go with static metadata member functions or non-template based polymorphism.

like image 31
gwiazdorrr Avatar answered Sep 30 '22 00:09

gwiazdorrr