如何前向声明一个C++模板类?

153

假设有以下模板类:

template<typename Type, typename IDType=typename Type::IDType>
class Mappings
{
public:
    ...
    Type valueFor(const IDType& id) { // return value }
    ...
};

如何在头文件中前向声明这个类?

4个回答

148

这是你应该这样做的方法:

template<typename Type, typename IDType=typename Type::IDType>
class Mappings;

template<typename Type, typename IDType>
class Mappings
{
public:
    ...
    Type valueFor(const IDType& id) { // return value }
    ...
};

请注意,默认情况下在前向声明中而不是实际定义中。


19
你可以声明一个模板类,其定义说明了默认参数,但是每次引用该类时,直到引入定义为止,必须包括所有参数。
例如,在不包含它的情况下使用结构体 Foo:
template <class>
struct Foo;

// Note that we *must* use the template here,
// even though in the definition it'll have a default.
template <class T> 
auto Func (Foo<T> foo)
{
    // do stuff with Foo before it's defined:
    return foo.Bar();
}

int Func (int n)
{
    return n;
}

我们可以进行编译,包含定义,例如:

int main ()
{
    return Func(3);
}

演示

...或者在包含定义后使用它,例如:

template <class T = bool>
struct Foo
{
    T Bar () {return 9;}
};

// Now the compiler understands how to handle
// Foo with no template arguments
// (making use of its default argument)

int main ()
{
    return Func(Foo<>());
}

演示

我没有检查过标准,但这适用于使用-std=c++98-std=c++17clang/gcc,因此如果它不是正式的标准,则看起来是非正式的。


尽管理论上这应该适用于namespace std,并且在我检查的示例中(使用许多编译器)似乎也是如此,但标准规定它是未定义行为:根据C++11标准,17.6.4.2.1:

除非另有规定,否则C++程序的行为将是未定义的,如果它添加了声明或定义到namespace stdnamespace std内的命名空间。

(我从一个SO答案中获得了这个信息。)

感谢Antonio在评论中指出这一点(并提供链接)。


也许你忘记了空的尖括号 Foo<> foo; - Oleksa
1
你不应该前置声明std::vector - Antonio

8

你只能为模板的第一个声明指定默认参数。如果你想允许用户前向声明一个类模板,你需要提供一个转发头文件。如果你想使用默认值前向声明别人的类模板,那就没戏了!


2

我的回答补充了其他回答,因为我发现的解决方案实际上通过创建一个新类型在所有参数已知时(或提供默认值)来减少了模板类前向声明的需求,因此这个新类型不再是一个模板,可以进行前向声明:

template<typename Type=MyDefault, typename IDType=typename Type::IDType>
class MappingsGeneric
{
...
};

class Mappings : public MappingsGeneric<> {};

你可以使用class Mappings;。我知道这种解决方案并不适用于所有情况,但在我的用例中,我仅在单元测试上下文中使用模板进行高性能依赖注入,用于非虚拟方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接