指向采用抽象参数的函数的指针

Pointer to function taking abstract parameter

设 A 为 C++ 中的抽象 class:

// legal
class A {
  virtual void m() = 0;
}

定义抽象类型的变量当然是非法的class:

A a; // illegal

确实如此,clang 3.6.0 抱怨:

wat.cc:5:3: error: variable type 'A' is an abstract class
A a;
  ^
wat.cc:2:16: note: unimplemented pure virtual method 'm' in 'A'
  virtual void m() = 0;
               ^
1 error generated.

同样,用抽象类型的参数定义函数也是非法的class:

void f(A a) {} // illegal

的确,clang 抱怨道:

wat.cc:5:10: error: parameter type 'A' is an abstract class
void f(A a) {}
         ^
wat.cc:2:16: note: unimplemented pure virtual method 'm' in 'A'
  virtual void m() = 0;
               ^
1 error generated.

另一方面,定义一个类型为指向抽象值指针的变量当然是合法的 class:

A * a_ptr; // legal

即使 class 从未定义任何基础 class,类型 A * 至少有一个值,nullptr,因此总是可以以类型正确的方式使用这样的变量,即使它可能不是很有用,除非 A 在某些时候得到一些具体的 subclasses:

a_ptr = nullptr; // legal

大概,这个推理适用于任何指针类型。

同样,定义一个接受参数的函数是合法的,该参数的类型是指向抽象值的指针class:

void f(A * a) {} // legal

同样,定义一个接受参数的函数是合法的,该参数的类型是指向函数的指针,该函数接受的参数类型是指向抽象值的指针class:

void f(void (* g)(A *)) {} // legal

这两个函数当然可以合法调用:

f(nullptr); // legal

据推测,函数参数的具体类型与类型的正确性无关,只要是指针类型即可。在任何一种情况下,对 f 的调用都应该是合法的。就类型论而言,格式良好的指针类型始终是合法的,并且 nullptr 始终存在于其中。根据这个推理,这个定义应该被推定为合法的:

void f(void (* g)(A)) {}

f 是一个接受参数的函数,该参数的类型是指向函数的指针,该函数接受的参数类型是抽象值 class A.

事实上,该参数的唯一可能值是 nullptr,因为没有实际函数可以采用抽象类型的参数 class。

这符合 C++ 标准吗?

Clang 抱怨:

wat.cc:5:20: error: parameter type 'A' is an abstract class
void f(void (* g)(A)) {}
                   ^
wat.cc:2:16: note: unimplemented pure virtual method 'm' in 'A'
  virtual void m() = 0;
               ^
1 error generated.

GCC 没有。

更新:这个问题回答正确,但问题本身在声明 Clang 接受该功能时是错误的。 GCC 和 Clang 都会在这段代码中产生类似的错误。但是,Clang 接受这样的函数 如果它们是成员函数 。这有效:

#include <iostream>

struct A {
  virtual ~A() = 0;
};

struct B {
  void f(void (*g)(A)) {
    std::cout << "Works!" << std::endl;
  }
};

int main() {
  B().f(nullptr);
  return 0;
}

不过,这似乎只是 Clang 中的一个错误。根据接受的答案中引用的标准部分中的语言,应该拒绝此代码。

[class.abstract]/3 说:

An abstract class shall not be used as a parameter type