【问题标题】:Is it possible to call functions with different amounts of params, passing the same parameter pack是否可以调用具有不同数量参数的函数,传递相同的参数包
【发布时间】:2018-06-23 10:55:57
【问题描述】:

例如说我有两个简单的功能:

void a(int x)
{
    //do something with x
}

void b(int x, float y)
{
    // do something with x and y
}

我希望有一个具有可变数量的参数的函数,可以根据标志调用上述两种方法:

template<typename... Args>
void varArgs(bool a_or_b, Args... args)
{
    if (a_or_b)
        a(args...);
    else
        b(args...);
}

该标志会告诉我们是要使用第一个函数还是第二个函数,但是由于模板是在编译时实例化的,所以这不起作用。我读过constexpr if,但是我只能使用 c++14,所以我想知道是否有替代方案?

编辑: bool 可以是编译时常量,而不是运行时参数。

【问题讨论】:

  • 如果没有大量的类型擦除,您将无法做您想做的事情,因为您在运行时传递了布尔值(并且constexpr if 通常不适用于该布尔值)。如果您将布尔值作为模板参数template &lt;bool B, typename ... Args&gt; void varArgs(Args... args) 传递,那么您可以通过标签调度轻松完成。
  • @NirFriedman 刚刚发现 bool 可以是编译时值,您介意发布一个答案,告诉我如何使用它吗?
  • 您是否考虑过“int main(int argc, char* argv[])” ... 具有不同数量参数的单个函数。在 C++ 中,按照同样的思路,我创建了“size_t grep (const std::string fileName, const std::string patterns, std::ostream& an_ostream = std::cout)。字符串参数,有人吗?
  • @DOUGLASO.MOEN 这就是我所说的更重的类型擦除。如果你想走那条路,你最好简单地绕过,例如std::vector&lt;std::any&gt;,或者更好,unordered_map&lt;string, any&gt;,至少这样您就不必将所有参数都转换为字符串并返回。

标签: c++ templates parameter-passing c++14 variadic-templates


【解决方案1】:

你可以用constexpr if 做的任何事情,你都可以用标签调度来做。它看起来像这样:

void a(int x)
{
    //do something with x
}

void b(int x, float y)
{
    // do something with x and y
}

template <typename ... Args>
void callImpl(std::true_type, Args && ... args) {
    a(std::forward<Args>(args)...);
};

template <typename ... Args>
void callImpl(std::false_type, Args && ... args) {
    b(std::forward<Args>(args)...);
};

template<bool callA, typename... Args>
void varArgs(Args&&... args)
{
    callImpl(std::integral_constant<bool, callA>{}, std::forward<Args>(args)...);
}

int main() {
    varArgs<true>(0);
    varArgs<false>(0, 0.0);
}

这里的想法是,从varArgscallImpl 的调用将根据布尔值的不同进行分派。为此,我们需要将布尔值提升为不同的类型,这就是为什么我说布尔值需要是模板参数而不是值的原因。实例:http://coliru.stacked-crooked.com/a/6c53bf7af87cdacc

【讨论】:

  • 老实说,我不明白为什么需要bool。您可以发送至sizeof...(args)。甚至添加一个漂亮的static_assert 错误消息。
  • @StoryTeller 是的,我想过。我不知道,取决于事物的结构,也许这就是 OP 更喜欢的。
【解决方案2】:

函数重载

如果您只是想根据传递参数的签名来调用参数包,您可以只为同一函数使用不同的重载:

void a(int x)
{
    //do something with x
}

void a(int x, float y)
{
    // do something with x and y
}

template<typename... Args>
void dispatcher(Args... args)
{
    a(args...);
}

标签调度

如果您想根据布尔值选择函数,并且总是有 2 个参数(1 个int,1 个float),您可以使用模板布尔值和标签调度来实现:

#include <type_traits>

void a(int x)
{
    //do something with x
}

void b(int x, float y)
{
    // do something with x and y
}

template<bool B, typename... Args>
void disp_impl(std::true_type, Args... args)
{
    a(args...);
}

template<bool B, typename... Args>
void disp_impl(std::false_type, Args... args)
{
    b(args...);
}

template<bool B, typename... Args>
void dispatcher(Args... args)
{
    using type = std::integer_constant<bool, B>;
    a(type{}, args...);
}

运行时

如果您需要运行时选择,则必须以老式方式进行。请记住,所有函数签名都必须有效,因为分支评估在编译时是未知的。因此,这仅在您将相同的参数传递给函数的所有版本并且布尔值在编译时未知时才有用。这依赖于get 描述的here 的帮助器。

void a(int x)
{
    //do something with x
}

void b(int x, float y)
{
    // do something with x and y
}

template<typename... Args>
void dispatcher(bool a_or_b, Args... args)
{
    if (a_or_b)
        a(get<0>(args...));
    else
        b(args...);
}

【讨论】:

  • 最终的运行时示例是否适用于如下调用:dispatcher(true, 1);。如果只有两个 args,它看起来将无法编译。
  • 您的运行时解决方案仍然无法正常工作;如果你执行dispatcher(true, 1);,它将无法编译而不是调用a。我可能只是说在运行时选择它更复杂。
  • @nitronoid 这就是 cmets 部分需要读写互斥锁的原因 ;-)
  • @NirFriedman,这是假设两个参数总是通过,我在解释中澄清了这一点。是的,它会编译失败。这假设您始终使用dispatcher(true, 1, 2.); 或类似名称进行呼叫。
  • @AlexanderHuszagh “我希望有一个具有可变数量的参数的函数,它可以根据标志调用上述两者:”。您的运行时实现不是那样。事实上,可变参数模板的使用是没有意义的,你可以直接取 int、float,而根本不将第二个参数传递给 a,这要简单得多,并且完全等同于这里发生的事情(当然,它清楚地表明它提供的价值很小)。
【解决方案3】:

很遗憾,这不是通用解决方案,而是针对您的特定情况量身定制的解决方案。

如果有几个 lambda 包装器,在 false 情况下第二个值的默认值是什么?

template<typename... Args>
void varArgs (bool a_or_b, Args... args)
 {
   if ( a_or_b )
      [](int x, ...){ a(x); }(args...);
   else
      [](int x, float y = 0.0f, ...){ b(x, y); }(args...);
 }

a_or_b 的值不是已知的编译时间时,这也应该有效。

如果你可以修改varArgs()签名,你可以直接使用第二个参数默认值技巧,避免使用lambda函数

void varArgs (bool a_or_b, int x, float y = 0.0f, ...)
 {
   if ( a_or_b )
      a(x);
   else
      b(x, y);
 }

【讨论】:

  • 这是我提供的,除了没有完全损坏。投赞成票。
  • @StoryTeller - 奇怪:通常我会尝试开发一个损坏的代码,然后你编写正确的代码。不过稍微想一想……也许这个版本过于复杂……第二个带默认值技巧的变量可以直接在varArgs()签名中使用……我必须再读一遍问题
猜你喜欢
  • 1970-01-01
  • 2018-03-26
  • 2018-02-07
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多