【问题标题】:non-type template parameter : how to pass reference to base class object?非类型模板参数:如何传递对基类对象的引用?
【发布时间】:2016-05-11 14:04:49
【问题描述】:

似乎无法将引用传递给 派生对象的基类对象作为模板参数, 正如我在这里尝试做的那样:

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;
//   ^

我本来期望'c'对象'default_a',因为它是从 'a',可以作为 'const a&' 被接受,因为它在第 16 行。

为什么这不能作为模板参数?

规范的哪一部分实际上要求这种行为?

也许我构建的 gcc-5.3.0 存在某种缺陷?

有人找到了将派生对象作为基类对象引用模板参数传递的好方法/方法吗?

我不能只用引用变量 'c_as_a' 代替 'default_a' 模板参数列表:

template < const a & The_A = c_as_a >

t.C:24:7: 错误:'const a& c_as_a' 不是类型 'const a&' 的有效模板参数,因为引用变量没有常量地址 b a_b;

我也不能替换任何类似的 constexpr 函数调用:

 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 ) >

因为此调用不是“具有外部链接的对象”。

任何关于如何实现将派生对象的基类的引用作为模板参数传递的建议将不胜感激 - 它是 必须是可能的,我只是看不到ATM。

必须有一种方法可以将对象的基类对象的引用指定为模板参数。

【问题讨论】:

  • 在 C++14 中,非类型模板参数 const a &amp; 只能通过链接直接绑定到命名的左值。所以你运气不好。在 C++17 中,这将被允许绑定到更多的东西,see here
  • 感谢 M.M - 很抱歉最初的“The_A = c”错字 - 现在更正了。
  • 好的,感谢您确认我的怀疑。
  • 实际上我不太确定,我在 C++14 标准中看不到为什么不应该接受 default_a。它是一个带链接的命名左值,它的地址是一个常量表达式
  • 可能是 [temp.arg.nontype]/5.3:“对于对象类型引用的非类型模板参数,不适用任何转换。” (因此不允许执行从派生到基础引用的转换)

标签: c++


【解决方案1】:

gcc 特定的解决方法:

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;

以上编译OK。

幸运的是,我们知道 '_default_c' 的名称没有被破坏。

【讨论】:

    【解决方案2】:

    问题是在初始化const 引用时会创建一个临时对象,并且在此上下文中引用初始化不允许使用临时对象(转换后的常量表达式)。

    N4140 8.5.3 (5.2.2.1)

    ...

    • 如果T1 是非类类型,则会从初始化表达式创建并复制初始化(8.5) 类型的“cv1 T1”临时。 然后将引用绑定到临时文件。

    在所有情况下,除了最后一个(即,创建和初始化一个 来自初始化表达式的临时),该引用被称为 直接绑定到初始化表达式。

    然后在 5.19:

    条件表达式 e 是一个核心常量表达式,除非 e的评估,遵循抽象机的规则 (1.9),将评估以下表达式之一:

    (2.9) - 一个 id-expression 引用引用类型的变量或数据成员,除非该引用具有前面的初始化和 要么

    • 它是用常量表达式初始化的,或者...

    【讨论】:

    • 啊哈!感谢您提供标准文档参考报价 - 我会进一步研究。我期待 C++-17 干净利落地解决这个问题——现在我的 gcc hack 就可以了。
    • @JVD 我在 N4140 中找不到明确表示“直接绑定”的语言,但它在 latest draft 中。稍后会更新答案。
    • 不幸的是,只需将上述片段放置在封闭的命名空间中即可 - 然后“_default_c”名称被破坏。
    • @JVD 阅读this
    • T 的引用绑定到T 类型的对象或从T 派生的对象时,不会创建临时对象。您引用了非类类型的案例,但 a 是类类型
    【解决方案3】:

    在命名空间中工作的修复:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-13
      • 2019-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多