给定一个模板类,如下所示:
template<typename Type, typename IDType=typename Type::IDType>
class Mappings
{
public:
...
Type valueFor(const IDType& id) { // return value }
...
};
有人如何在头文件中转发声明这个类?
你会这样做:
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 内的命名空间,则 C++ 程序的行为是未定义的,除非另有说明。
(我从 SO answer 获得此信息)。
感谢 Antonio 在评论中指出这一点(并提供链接)。
您只能为模板的第一个声明声明模板的默认参数。如果你想让用户转发声明一个类模板,你应该提供一个转发头。如果您想使用默认值转发声明其他人的类模板,那么您就不走运了!
我的答案补充了其他答案,因为我发现的解决方案实际上通过在所有参数已知(或作为默认参数提供)时创建一个新类型来减轻对模板类前向声明的需求,因此这种新类型,而不是你可以前向声明的,不是不再是模板:
template<typename Type=MyDefault, typename IDType=typename Type::IDType>
class MappingsGeneric
{
...
};
class Mappings : public MappingsGeneric<> {};
然后您可以class Mappings;
。我知道这个解决方案并不适用于任何地方,但它适用于我的用例,因为我只在单元测试上下文中为非虚拟方法使用高性能依赖注入模板。
不定期副业成功案例分享
Foo<> foo;
。std::vector
stackoverflow.com/a/10290176/2436175