C++:我可以在模板参数包中强制执行至少1个agment吗

C++: Can I enforce at least 1 agument in a template parameter pack

本文关键字:强制执行 1个 agment 包中 参数 我可以 C++      更新时间:2023-10-16

这更像是我正在考虑的设计约束。这段代码针对的是虚幻引擎,以防您对语法感到疑惑,但这应该与问题无关。

这个模板代码只需获取用户想要实例化的对象类型,然后创建一个拥有的容器对象,并对所有者调用add方法。(业主负责实际施工逻辑(

template<typename T>
void UEntityFactory::Create_Impl(AEntityObject* owner)
{
static_assert( TPointerIsConvertibleFromTo<T, UEntity>::Value, "[UEntityFactory] ERROR: Attempting to create a non-entity type");
owner->AddEntity<T>();
}
// Templated create accepting variadic arguments; it first creates the housing entity object
// and then recursively generates code for each argument.
template <typename T, typename...Args>
AEntityObject* UEntityFactory::Create()
{
// Create the owning EntityObject
AEntityObject* obj = NewObject<AEntityObject>(this);
Create_Impl<T>(obj);
// Recursively call Create_Impl for Args
Create_Impl<Args...>(obj);
return obj;
}

根据我从模板参数包中了解到的情况,可变参数(在我的例子中是Args(是可选的。使用两个类型名而不是一个参数包的基本原理是确保用户至少传递一个模板参数。

我想限制创建过程,以确保始终创建有效的对象。但是,如果我调用以下行,上面的代码就不会编译:

AEntityObject* obj = factory.Create<EntityA>();

我认为,对于我如何设置模板声明,它需要另一个参数。错误如下:

  • "UEntityFactory::Create_Impl":未找到匹配的重载函数
  • 注意:请参阅正在编译的函数模板实例化"AEntityObject *UEntityFactory::Create<UDebugEntityA,>(void)"的参考
  • "void UEntityFactory::Create_Impl(AEntityObject *)":无法推导出"T"的模板参数

这是我知道为什么会发生的错误之一,但我无法阐明。当然,如果我将Create函数的声明更改为以下内容,它会编译得很好:

template <typename...Args>
AEntityObject* UEntityFactory::Create()
{
AEntityObject* obj = NewObject<AEntityObject>(this);
// Recursively call Create_Impl for Args
Create_Impl<Args...>(obj);
return obj;
}

然而,这意味着,用户现在可以编写

AEntityObject* obj = factory.Create<>();

这就是我想要避免的。有没有一种优雅的方法可以强制至少一个模板参数,以及一个可选的集变量参数?我不能重载Create方法,因为这将创建一个不明确的调用。

编辑:我搞砸了,我错误地认为我的参数一开始就正确展开了——感谢Jarod42的更正。

好吧,你只需要添加一个static_assert()来进行检查,还可以发出一个更有意义的编译时错误:

template <typename...Args>
AEntityObject* UEntityFactory::Create()
{
static_assert(sizeof...(Args) > 0, "Create() would like to have at least one object type to create...");
AEntityObject* obj = NewObject<AEntityObject>(this);
// Recursively call Create_Impl for Args
Create_Impl<Args...>(obj);
return obj;
}
template <typename T, typename...Args>
AEntityObject* UEntityFactory::Create()
{
// [..]
// Recursively call Create_Impl for Args
Create_Impl<Args...>(obj);
}

不是递归调用

因此,事实上,您似乎很难在可变模板上迭代

然后您可以使用:

template <typename T, typename...Args>
AEntityObject* UEntityFactory::Create()
{
// Create the owning EntityObject
AEntityObject* obj = NewObject<AEntityObject>(this);
Create_Impl<T>(obj);
// C++17: folding expression
(Create_Impl<Args>(obj), ...);
return obj;
}

和之前:

template <typename T, typename...Args>
AEntityObject* UEntityFactory::Create()
{
// Create the owning EntityObject
AEntityObject* obj = NewObject<AEntityObject>(this);
Create_Impl<T>(obj);
const int dummy[] = { 0, (Create_Impl<Args>(obj), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
return obj;
}