【问题标题】:Templates handling functions "returning" more than one value模板处理函数“返回”多个值
【发布时间】:2013-09-10 16:08:01
【问题描述】:

有时您希望函数返回多个值。一种非常常见的方法 在 C++ 中实现这样的行为是通过非常量引用传递您的值,并且 在你的函数中分配给他们:

void foo(int & a, int & b)
{
    a = 1; b = 2;
}

你会使用哪个:

int a, b;
foo(a, b);
// do something with a and b

现在我有一个函子可以接受这样的函数并希望转发 将参数设置为另一个返回结果的函数:

template <typename F, typename G>
struct calc;

template <
    typename R, typename ... FArgs,
    typename G
>
struct calc<R (FArgs...), G>
{
    using f_type = R (*)(FArgs...);
    using g_type = G *;

    R operator()(f_type f, g_type g) const
    {
        // I would need to declare each type in FArgs
        // dummy:
        Args ... args;
        // now use the multiple value returning function
        g(args...);
        // and pass the arguments on
        return f(args...);
    }
};

这种方法是否有意义,或者我应该使用基于元组的 方法?这里有比基于元组的方法更聪明的方法吗?

【问题讨论】:

  • 我想这取决于用例。使用std::tuple 似乎更容易。您也可以使用std::tuple 作为持有者,并且仍然使用其成员来保持fg 原样。如果您需要这方面的帮助,请告诉我,我可以发布一些内容。
  • 我真的不喜欢通过引用函数参数返回值的方法——返回一个元组(你可能会得到一些 RVO 或 r 值引用)。
  • @DanielFrey 我已经对std::tuple 添加了一个非常幼稚的更改,但我仍然对至少让f 保持不变以便能够使用定义在标准命名空间。我同意按照 Dieter Lücking 的论证,为我的生成器返回 std::tuple 确实有意义。
  • 在第一个更简单的示例中,通常您只需创建一个 a、b 的结构并返回该结构,而不是传递内部修改的 refs。您能否将这个想法扩展到您的模板函数 - 将返回类型设为模板 arg 并在调用它时从仿函数返回值。
  • 同意 Deiter - 更改 ref 的值可能会让您所在班级的用户感到惊讶。

标签: c++ templates c++11


【解决方案1】:

您可以使用编译时索引:

template< std::size_t... Ns >
struct indices
{
    typedef indices< Ns..., sizeof...( Ns ) > next;
};

template< std::size_t N >
struct make_indices
{
    typedef typename make_indices< N - 1 >::type::next type;
};

template<>
struct make_indices< 0 >
{
    typedef indices<> type;
};

template< typename F, typename G >
struct calc;

template<
   typename R, typename ... FArgs,
   typename G
>
struct calc< R (FArgs...), G >
{
   using f_type = R (*)(FArgs...);
   using g_type = G *;

private:
   template< std::size_t... Ns >
   R impl(f_type f, g_type g, indices< Ns... > ) const
   {
      std::tuple< FArgs ... > args;
      g( std::get< Ns >( args )... );

      // alternatively, if g() returns the tuple use:
      // auto args = g();

      return f( std::get< Ns >( args )... );
   }

public:
   R operator()(f_type f, g_type g) const
   {
      return impl( f, g, typename make_indices< sizeof...( FArgs ) >::type() );
   }
};

【讨论】:

  • 这很整洁。我什至不知道你可以有编译时间索引。
  • @nijansen 它们可以是非常 useful 并且它们将在 C++14 中标准化(尽管名称略有不同)。我只是希望这种解决方法不是必需的,并且在解包时会有一种更直接的方法来从参数包中访问索引......
  • 我确实改变了一件事:我专门针对R (FArgs...)std::tuple&lt;ArgTypes...&gt; (),因为这样它与将参数作为常量引用或右值引用的f 配合得更好。
【解决方案2】:

当接受我们正在更改 fg 的签名以与 std::tuple 一起使用这一事实时,这个问题的答案就变得微不足道了:

template <typename F, typename G> struct calc;

template <typename R, typename ... Args>
struct calc<R (std::tuple<Args...> const &), std::tuple<Args...> ()>
{
    using f_type = R (*)(std::tuple<Args...> const &);
    using g_type = std::tuple<Args...> (*)();

    R operator()(f_type f, g_type g) const
    {
        return f(g());
    }
};

这是一个简单的例子:

int sum(std::tuple<int, int> const & t) { return std::get<0>(t) + std::get<1>(t); }
std::tuple<int, int> gen() { return std::make_tuple<int, int>(1, 2); }

auto x = calc<decltype(sum), decltype(gen)>()(&sum, &gen);

但是,此解决方案的局限性很明显:您必须编写自己的函数。使用这种方法是不可能使用像std::pow 这样的f

【讨论】:

  • 我不确定我会称之为“微不足道”。现在 C++ 已经认不出来了。
  • @LightnessRacesinOrbit 概念上微不足道,而不是语法上微不足道。另外,你变老了:/
猜你喜欢
  • 2021-07-19
  • 2015-08-25
  • 2019-01-06
  • 1970-01-01
  • 1970-01-01
  • 2019-06-22
  • 2020-12-17
  • 1970-01-01
  • 2018-07-18
相关资源
最近更新 更多