c++只允许用函数声明实例化对象



下面是简单的代码

class Base{
public:
int fcn();
};
int main() {
Base b; // clause 1
}

为什么可以编译?子句1创建了一个Base实例,但是fcn()函数没有定义。

Base b;不是编译器错误的原因是编译器通常无法知道是否缺少定义。

您发布的代码可能是完整的翻译单元,而定义在不同的翻译单元中。只有当需要定义(例如调用函数)但找不到定义时,链接器才会发出错误。


实际上有很多情况需要声明但不定义(或仅有条件定义)的东西。下面是两个例子。


假设您有一个带有double参数的方法,并且您希望防止用户使用int调用它。隐式转换可能很烦人,基本类型的隐式转换就更麻烦了。可以这样做:

struct foo {
void do_something(double) {}
};
struct bar {
void do_something(double) {}
void do_something(int);      // no definition !!
};

int main()
{
foo{}.do_something(1);
bar{}.do_something(1);
}

foo::do_something(double)可以和int一起调用。另一方面,bar::do_something(double)必须在重载解析上与bar::do_something(int)竞争,而bar{}.do_something(1);会导致链接器错误。

请注意,这里有更好的方法来获得更好的编译器错误消息(自c++ 11以来的= delete)。然而,关键是:只要您只使用double调用bar::do_something,一切都没问题。没有错误。而且不会出现错误。


另一个例子是用于区分模板的不同实例化的标签类型:

struct tag1;   // no definition !!
struct tag2;   // no defniition !!
template <typename T> struct foo;
template <> struct foo<tag1> { /* something */ };
template <> struct foo<tag2> { /* something else */ };
int main() {
foo<tag1> a;
foo<tag2> b;
}

这完全没问题,因为模板不做任何需要其实参为完整类型的操作。使用类型仅仅标记模板的实例化或选择重载是一种常见的技术,有时你所需要的标记只是一个声明。

诚然,这与您的示例不同,因为它缺少整个类定义,并且没有创建类的实例。虽然我会把它放在同一个袋子里:有一个没有定义的声明很有用。

最新更新