如何在成员函数中使用静态断言?

时间:2017-09-21 10:35:36

标签: c++ inheritance static-assert

我有以下方案:

struct Baz {};
struct Qux {};

struct Base {
  virtual ~Base() {}
  virtual void foo() = 0;
};

template<typename T> struct Identity { static bool const value = false; };
template<typename T> void bar(T) { static_assert(Identity<T>::value, "Busted!!!"); }
template<> void bar<Baz>(Baz) {}

template<typename T>
struct Derived : Base {
  T m;
  void foo() { bar(m); }
};

int main() {
  Base *b0 = new Derived<Baz>;
  b0->foo();
  Base *b1 = new Derived<Qux>;
  (void) b1;
}

也就是说,我有一个纯虚拟类Base和一个继承自Derived的模板类Base,并根据需要覆盖纯虚函数foo。现在,在foo内,我调用了函数模板barbar具有类Baz的专门化,但不适用于类Qux。在main时,我试图实现Derived<Baz>一切对象的确定。但是当我尝试实现Derived<Qux>编译器点击static_assert的对象时。

问:

有没有办法转换我的代码,只有在Derived<Qux>被调用时编译器才会在Derived<Qux>::foo()中命中静态断言。

即,实现Derived<Qux>的对象将通过:

Base *b1 = new Derived<Qux>;

但是后来在代码中程序员试图调用:

b1->foo(); // compile error static assert

2 个答案:

答案 0 :(得分:10)

标准在[temp.inst]/9说了一个有趣的事情:

  

实现不应隐式实例化函数   模板,变量模板,成员模板,非虚拟成员   function,成员类,类模板的静态数据成员,或   constexpr if语句的子语句,除非这样的实例化   是必须的。没有具体说明是否实施   隐式实例化类模板的虚拟成员函数   如果虚拟成员函数不会被实例化。

实例化虚拟功能的决定由实现决定,但前提是不需要。因此,我们面临的问题是:根据标准本身何时需要定义?

答案是[class.virtual]/11[temp.inst]/2

  

在类中声明的虚函数应该在该类中定义或声明为纯,或者两者都是;无需诊断

     

类模板特化的隐式实例化会导致声明的隐式实例化,而不会导致类成员函数的定义,默认参数或noexcept-specifiers

因此,类模板的任何实例化都将实例化Derived::foo的声明,这通过连锁反应需要定义。因此,定义也必须实例化,如果可用的话。

实现可以在第一个引用的段落中给出它的余地的唯一方法是Derived::foo是否也是纯虚拟的。例如,ClangGCC都可以做到这一点。那当然可能对你的帮助有限。

所以长话短说,只要功能是虚拟的(而不是纯虚拟的),它就是一个不起作用的。

答案 1 :(得分:1)

@StoryTeller提供了详细的答案,参考了规范等,但我会回过头来回答你的问题并询问你真正想要做什么。正如问题所写的那样,很明显,答案是“#34; no&#34;因为您要求在运行时才能确定的编译时错误。 E.g:

Base *b;
if (user_input() == 42) {
     b = new Derived<Baz>();
} else {
     b = new Derived<Qux>();
}
b->foo();

您是否希望此情况下出现编译器错误?如果是这样,您需要定义您认为Qux::foo应被视为&#34;被调用的条件。&#34;目前,编译器假定在实例化类中定义为虚拟的方法被调用。显然你想要一些不那么保守的东西,但是什么呢?

如果您有更具体的编译时类型信息,则可以在运行时捕获错误。 E.g。

Derived<Qux> d = new Derived<Qux>();
d->foo();

如果foo是非虚拟模板化方法,则可以在编译时验证基类型,然后调度到虚方法。 (可能需要更改foo的签名才能以某种方式使用该类型。)

更好的解决方案是将接口中的不同类型的功能分解为不同的类,并引入一种机制来获取给定具体类的特定接口。如果您具有手头的具体类型类并且在运行时具有动态查找接口,则可以在编译时检查。