如何转发声明一个 C + + 模板类?

给定如下模板类:

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

如何在头文件中转发声明这个类?

120801 次浏览

你应该这么做:

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

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

只能为模板的第一个声明声明模板的默认参数。如果希望允许用户转发声明类模板,则应提供转发标头。如果您想使用默认值转发声明其他人的类模板,那么您就不走运了!

可以声明的模板类的定义声明了默认参数,但是每次引用该类时,都必须包含其所有参数,直到引入定义为止。

让我们使用 struct 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<>());
}

小样

我还没有检查的标准,但这工程的 clang/gcc-std=c++98-std=c++17,所以如果它不是正式的标准,那么它看起来是非正式的。


虽然在原则上这应该适用于 namespace std,并且在我检查过的例子中出现过(在许多编译器中) ,但是标准声明它是未定义的行为: 根据 C + + 11标准,17.6.4.2.1:

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

(我从 回答我得到这个信息)。

感谢 安东尼奥在评论中指出这一点(并提供链接)。

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

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


class Mappings : public MappingsGeneric<> {};

然后你可以 class Mappings;。我知道这个解决方案并不适用于所有地方,但在我的用例中确实适用,因为我只在单元测试上下文中为非虚拟方法使用高性能依赖注入模板。