4

由于我选择了具有误导性的问题标题,因此该问题已作为完全重复的问题关闭。这没有错,但提出了一个经常讨论的问题,例如在这个问题中。由于内容是关于 Stackoverflow 从未涵盖的更具体的主题,我希望重新打开这个问题。这发生在现在,所以问题来了。

我给出了一个期望三个整数值作为参数的函数length(int x, int y, int z);。我不能修改这个函数,例如接受一个结构或元组作为单个参数。

在 C++ 中有没有办法编写另一个函数,它可以用作上面函数的单个参数,比如length(arguments());

无论如何,该函数的返回类型arguments();似乎需要是int, int, int. 但据我所知,我无法在 C++ 中定义和使用这样的函数。我知道我可以返回一个列表、一个元组、一个结构或一个类arguments()。这个问题被关闭了,因为有些人认为我会问这个问题。但困难的部分是传递元组、结构或其他三个给定的整数参数。

这可能吗?如果可以,在 C++ 中怎么可能?使用 C++11 的解决方案会很好。

4

6 回答 6

8

我认为没有任何直接的方法可以做你想做的事,但这是我在代码的几个地方使用的 C++11 技术。基本思想是使用我调用的模板函数call_on_tuple来获取函数参数f以及进一步参数的元组,扩展元组并在扩展的参数列表上调用函数:

template <typename Fun, typename... Args, unsigned... Is>
typename std::result_of<Fun(Args...)>::type
call_on_tuple(Fun&& f, std::tuple<Args...>&& tup, indices<Is...>)
{ return f(std::get<Is>(tup)...); }

所以这个想法是,而不是打电话

length(arguments());

你会打电话给

call_on_tuple(length,arguments());

这假设它arguments()已更改,因此它返回 a std::tuple<int,int,int>(这基本上是您引用的问题的想法)。

现在困难的部分是如何获取Is...参数包,它是一个整数包,0,1,2,...用于对元组的元素进行编号。

如果你确定你总是有三个参数,你可以0,1,2按字面意思使用,但如果你的目标是让它适用于任何 n 元函数,我们需要另一个技巧,其他帖子已经描述了这个技巧,例如在几个这个帖子的答案。

将参数的数量sizeof...(Args)转换为整数列表0,1,...,sizeof...(Args)是一个技巧:

我将把这个技巧和实现call_on_tuple放在一个命名空间中detail

namespace detail {

  template <unsigned... Is>
  struct indices
  { };

  template <unsigned N, unsigned... Is>
  struct index_maker : index_maker<N-1,N-1,Is...>
  { };

  template <unsigned... Is>
  struct index_maker<0,Is...>
  { typedef indices<Is...> type; };


  template <typename Fun, typename... Args, unsigned... Is>
  typename std::enable_if<!std::is_void<typename std::result_of<Fun(Args...)>::type>::value,
              typename std::result_of<Fun(Args...)>::type>::type
  call_on_tuple(Fun&& f, std::tuple<Args...>&& tup, indices<Is...>)
  { return f(std::get<Is>(tup)...); }
}

现在实际函数call_on_tuple在全局命名空间中定义如下:

template <typename Fun, typename... Args>
typename std::enable_if<!std::is_void<typename std::result_of<Fun(Args...)>::type>::value,
            typename std::result_of<Fun(Args...)>::type>::type
call_on_tuple(Fun&& f, std::tuple<Args...>&& tup)
{
  using std::tuple;
  using std::forward;
  using detail::index_maker;

  return detail::call_on_tuple
    (forward<Fun>(f),forward<tuple<Args...>>(tup),typename index_maker<sizeof...(Args)>::type());
}

它基本上调用detail::index_maker生成递增整数列表,然后调用detail::call_on_tuple它。

因此,您可以这样做:

int length(int x, int y, int z)
{ return x + y + z; }

std::tuple<int,int,int> arguments()
{ return std::tuple<int,int,int> { 1 , 2 , 3 }; }

int main()
{
  std::cout << call_on_tuple(length,arguments()) << std::endl;
  return 0;
}

希望这与您需要的足够接近。

笔记。我还添加了一个enable_if以确保仅与f实际返回值的函数一起使用。您可以轻松地为返回的函数创建另一个实现void

再次抱歉过早地结束您的问题。

PS。您需要添加以下包含语句来测试它:

#include <tuple>
#include <type_traits>
#include <iostream>
于 2012-12-30T10:28:58.297 回答
4

这是不可能的,C++ 不允许本机提供 3 个返回值,这些返回值可以用作另一个函数的 3 个单独的输入参数。

但是有返回多个值的“技巧”。length()尽管这些都没有为您的问题提供完美的解决方案,因为它们不能在不修改的情况下用作单个参数length()

使用容器对象,如struct,tupleclass

typedef struct { int a,b,c; } myContainer;

myContainer arguments(int x, int y, int z) {
  myContainer result;
  result.a = 1;
  // etc
  return result;
}

myContainer c = arguments(x, y, z);
length(c.a, c.b, c.c);

诀窍是重载length()函数,所以看起来你可以用一个参数来使用它:

void length(myContainer c) {
  length(c.a, c.b, c.c);
}

length(arguments());

当然,您可以通过使用inline、宏等进一步优化它。

我知道这仍然不是您想要的,但我认为这是最接近的方法。

于 2012-12-30T09:26:29.220 回答
1

你需要声明一个struct { int a, b, c; }或类似的东西(一个类也可以) - 我认为你一直在编程 python 或 php 或类似的东西。

于 2012-12-30T09:22:50.340 回答
1

大多数编程语言会通过某种形式的适配器函数来做到这一点。这是一个将要调用的函数(此处length)和调用它的参数作为参数的函数。您可能可以使用模板在 C++ 中构建类似的东西。查看functional标题以获得灵感。

Perl 是一种本机提供您正在寻找的语言的语言。你可以写:

sub arguments {
    return 1, 2, 3;
}
sub length {
    my ($p1, $p2, $p3) = @_;
    # … Work with $p1, $p2 and $p3
}
length(arguments());
于 2012-12-30T09:42:02.530 回答
-1

通过引用传递参数,这样您就可以在不返回或返回结构的情况下更改它们。您只能从函数返回单个值。

于 2012-12-30T09:24:29.493 回答
-2

我们只能返回一个值。但如果您想返回多个值,您可以使用数组或定义对象或结构

    int* arguments() {
        int x[1,4,6] 
        return x;
    };

    void length(int i[]);

    length(arguments());
于 2012-12-30T09:36:22.377 回答