【问题标题】:C++ nontype template argument taking inherited classC++ 非类型模板参数采用继承类
【发布时间】:2012-11-28 13:43:32
【问题描述】:

我希望能够将从DBMetaData 继承的对象的引用作为另一个类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;
}

我可以编译和运行上面的例子但是我需要明确指定继承类型。

如果我尝试编译它定义DO_WHAT_I_WANT(我想将DBMetaData 类型的引用或指针传递给任何继承类的对象),我会收到错误:

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

为什么我不能传递_u1,即继承自DBMetaDataDBMetaData_NT 类型作为DBVar&lt; _md_u1 &gt; _u1; 的参数?

有什么方法可以得到我想要的吗?

谢谢!


编辑:

按照@ecatmur 的建议,用函数指针替换模板参数解决了我的问题,而且我必须注意,这让我的代码更具可读性。

#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;
}

【问题讨论】:

    标签: c++ templates inheritance template-specialization


    【解决方案1】:

    很遗憾没有。根据 14.3.2 模板非类型参数,第 1 段:

    非类型、非模板的模板参数应为以下之一:[...]

    • 一个常量表达式,指定具有静态存储持续时间的对象的地址 [...],表示(忽略括号)为 &amp;id-expression,除了 &amp; [ ...] 如果对应的 template-parameter 是引用,则应省略。

    根据同一部分的第 5 段,不允许进行派生到基础的转换:

    • 对于对象类型引用的非类型模板参数,不适用任何转换。引用所指的类型可能比 template-argument 的(否则相同)类型更具有 cv 限定。 template-parameter 直接绑定到 template-argument,它应该是一个左值。

    这也意味着不允许进行强制转换,因为这不是 [&amp;] id-expression 的形式,并且不会产生左值。

    根据您要完成的任务,您可以通过手动模拟多态性来获得类似的结果,例如将_md_u1 初始化为函数的返回值,该函数设置适当的 vtable 指针或表。

    【讨论】:

    • 就是这样。一个函数。它解决了我的问题。我用基于函数的解决方案编辑了我的问题。谢谢!
    • 我想知道术语“协方差”是否适用于此。众所周知 (stackoverflow.com/questions/2203388/c-templates-polymorphism) C++ 模板不是协变的。似乎可以说模板非类型参数也不是协变的。
    【解决方案2】:

    您可以使用 C++11 的 decltype 和如下宏:

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

    现在,定义一个变量:

    DBVAR(_md_u1) _u1;
    

    希望这会有所帮助。

    编辑:老实说,我不喜欢你的做法。我宁愿使用静态元数据成员函数或非基于模板的多态性。

    【讨论】:

      猜你喜欢
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      • 2014-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-31
      相关资源
      最近更新 更多