简体   繁体   English

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

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

This is more of a design constraint I am considering. 这更多是我正在考虑的设计约束。 This snippet of code is targeting the Unreal Engine in case you are wondering about the syntax - but that should be irrelevant to the question. 如果您想了解语法,此代码段针对的是虚幻引擎-但这与问题无关。

This template code simply takes the types of objects the user wants instantiated - it then creates an owning container object and calls the add method on the owner. 该模板代码仅采用用户想要实例化的对象类型-然后创建一个拥有容器的对象并在所有者上调用add方法。 (The owner is responsible for the actual construction logic) (业主负责实际的施工逻辑)

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

From what I understood from template parameter packs, the variadic argument (...Args in my case) is optional. 据我从模板参数包中了解到,可变参数(在我的情况下为... Args)是可选的。 The rationale behind having two typenames as opposed to one parameter pack was to ensure that the user at LEAST passes one template argument. 具有两个类型名而不是一个参数包的基本原理是确保LEAST的用户传递一个模板参数。

I want to restrict the creation process to ensure a valid object is always created. 我想限制创建过程,以确保始终创建有效的对象。 But the code above does not compile if I invoke a line such as: 但是,如果我调用如下代码,则上面的代码将无法编译:

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

I think with how I set my template declaration, it requires another argument. 我认为在设置模板声明的方式上,它需要另一个参数。 The error is as follows: 错误如下:

  • ' UEntityFactory::Create_Impl ': no matching overloaded function found ' UEntityFactory::Create_Impl ':找不到匹配的重载函数
  • note: see reference to function template instantiation ' AEntityObject *UEntityFactory::Create<UDebugEntityA,>(void) ' being compiled 注意:请参见对正在编译的函数模板实例化' AEntityObject *UEntityFactory::Create<UDebugEntityA,>(void) '的AEntityObject *UEntityFactory::Create<UDebugEntityA,>(void)
  • ' void UEntityFactory::Create_Impl(AEntityObject *) ': could not deduce template argument for 'T' ' void UEntityFactory::Create_Impl(AEntityObject *) ':无法推断'T'的模板参数

This is one of those errors that I kinda know why it happens but I cannot articulate it. 这是我很清楚为什么会发生但我无法明确表述的那些错误之一。 Surely enough, if I change the declaration of the Create function to the below, it compiles fine: 果然,如果我将Create函数的声明更改为以下内容,则可以正常编译:

template <typename...Args>
AEntityObject* UEntityFactory::Create()
{
    AEntityObject* obj = NewObject<AEntityObject>(this);

    // Recursively call Create_Impl for Args
    Create_Impl<Args...>(obj);
    return obj;
}

However the implications of this is, the user can now write 但是,这样做的含义是,用户现在可以编写

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

Which is what I want to avoid. 这是我要避免的。 Is there an elegant way to force at least one template argument, alongside an optional set variadic argument? 是否有一种优雅的方法来强制至少一个模板参数以及一个可选的可变参数参数集? I cannot overload the Create method as that will create an ambiguous call. 我无法重载Create方法,因为这将创建一个模糊的调用。

EDIT: I messed up, I falsely assumed my parameters were being unfolded correctly to begin with - thanks Jarod42 for correcting. 编辑:我搞砸了,我错误地以为我的参数正确地开始以-谢谢Jarod42进行更正。

Well, you could just add a static_assert() to do the check and also issue a more meaningful compile-time error: 好吧,您可以仅添加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);
}

Is not a recursive call 不是递归调用

So in fact it seems you have difficulty to iterate over your variadic template. 因此,实际上,您似乎很难遍历可变参数模板。

You might then use: 然后,您可以使用:

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

and before: 和之前:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM