具有多个返回类型的函数

Function with multiple return types

本文关键字:函数 返回类型      更新时间:2023-10-16

我有两个枚举,它们基本上决定(在运行时)要做什么。"映射"看起来像

struct Foo { class CA; class CB; class CC; CA a; CB b; CC c; };
enum Base { A, B, C };
enum Func { X, Y };
Foo foo;
// A, X => use(foo.a.x());
// A, Y => use(foo.a.y());
// B, X => use(foo.b.x());
// B, Y => use(foo.b.y());

问题是,abC,以及x()y()的返回类型都是不同的类型(有些模板类型真的很大)。

使用开关或if映射两个枚举是非常丑陋的,需要大量的工作,所以我想知道,如果我能以某种方式写这样的东西:

struct Foo { class CA; class CB; class CC; CA a; CB b; CC c; };
enum Base { A, B, C, };
enum Func { X, Y, }; 
template <typename T> auto applyFunc(Func f, T t)
{
    switch(f)
    {
        case X: return t.x();
        case Y: return t.y();
    }
}

auto getBase(Base b, Foo f)
{
    switch(b)
    {
        case A: return f.a;
        case B: return f.b;
        case C: return f.c;
    }
}

Func f;
Base b;
Foo foo;
use(applyFunc(f, getBase(b, foo)));

编辑1:我不能编辑CA, CBCC类。我也不能编辑x()y()的类/返回类型。所有这些类型都来自外部库。

可以使用延续传递样式

template <class F> void applyFunc(WhichFunc w, T t, F f)
{
  switch(w)
  {
    case X: f(t.x());
    case Y: f(t.y());
  }
}
template<class F>
void getBase(Base b, Foo foo, F f)
{
  switch(b)
  {
    case A: f(foo.a);
    case B: f(foo.b);
    case C: f(foo.c);
  }
}

其中不返回,而是将下一步作为参数传递给前一步。

Func f;
Base b;
Foo foo;
getBase(b, [&](auto&&b){ applyFunc( f, b, [&](auto&& r){ use(r); } ); } );

或类似的东西(可能有错别字)。

std::variant(或boost)可用于将延续移动到返回值之后。

auto b = getBase(b);
auto r = visit( b, [&](auto&& b){ return applyFunc( f, b ); } );
visit( r, [](auto&& r){ use(r); } );

,上面的每个都返回一个variant<possible_return_types_go_here>,而不是一个延续。

简而言之:你不能。函数的返回类型必须在编译时是已知的。事实上,试图在一个函数中返回不同类型的auto返回类型,它会给你一个编译错误,即使你使用模板。

可能你的解决方案包括使用多态

另一个解决方案是使用traits:

struct Foo { class CA {}; class CB {}; class CC {}; CA a; CB b; CC c; };
enum Base { A, B, C, };
enum Func { X, Y, }; 
template<Base> struct GetBaseTraits;
template<> struct GetBaseTraits<Base::A> { using type = Foo::CA; };
template<> struct GetBaseTraits<Base::B> { using type = Foo::CB; };
template<> struct GetBaseTraits<Base::C> { using type = Foo::CC; };
template<Base b>
typename GetBaseTraits<b>::type getBase(Foo f);
template<> typename GetBaseTraits<A>::type getBase<A>(Foo f) { return f.a; }
template<> typename GetBaseTraits<B>::type getBase<B>(Foo f) { return f.b; }
template<> typename GetBaseTraits<C>::type getBase<C>(Foo f) { return f.c; }
int main() {
    Foo f{};
    Foo::CA ca = getBase<A>(f);
    Foo::CB cb = getBase<B>(f);
    Foo::CC cc = getBase<C>(f);
}

函数应始终返回具有相同数据类型的值,这些值在编译时确定。您可以使用某种形式的变体,例如boost::variant,或者创建您自己的变体类型:

enum ReturnType {
  RET_STR, RET_INT, RET_DBL
};
struct variant {
  ReturnType valtype;
  string str;
  int i;
  double d;
};
string f1() { ... }
int f2() { ... }
double f3() { ... }
variant f(int x) {
  variant v;
  switch (x) {
   case 0:
    v.valtype = RET_STR;
    v.str = f1();
    break;
   case 1:
    v.valtype = RET_INT;
    v.str = f2();
    break;
   case 2:
    v.valtype = RET_DBL;
    v.str = f3();
    break;
  }
  return v;
}

或者,您可以使用某种形式的多态性:

class result_handler {
public:
  virtual ~result_handler(){}
  virtual void handle_string(string s) = 0;
  virtual void handle_int(int i) = 0;
  virtual void handle_double(double d) = 0;
};
void f(int x, result_handler* h) {
  switch (x) {
    case 0:
      h->handle_string(f1());
      break;
    case 1:
      h->handle_int(f2());
      break;
    case 2:
      h->handle_double(f3());
      break;
  }
}
class my_result_handler : public result_handler {
public:
  virtual void handle_string(string s) { cout << "string " << s << endl; }
  virtual void handle_int(int i) { cout << "int " << i << endl; }
  virtual void handle_double(double d) { cout << "double " << d << endl; }
};

如果参数在编译时已知,则可以使用标记调度:

template <Base> BaseTag{};
decltype(auto) getBaseImpl(Foo& foo, BaseTag<Base::A>) { return foo.a; }
decltype(auto) getBaseImpl(Foo& foo, BaseTag<Base::B>) { return foo.b; }
decltype(auto) getBaseImpl(Foo& foo, BaseTag<Base::C>) { return foo.c; }
template <Base base>
decltype(auto) getBase(Foo& foo) { return getBaseImpl(foo, BaseTag<base>{}); }
template <Func> FuncTag{};
template <typename T>
decltype(auto) getFuncImpl(T& t, FuncTag<Func::X>) { return t.x(); }
template <typename T>
decltype(auto) getFuncImpl(T& t, FuncTag<Func::Y>) { return t.y(); }
template <typename T, Func func>
decltype(auto) getFunc(T& t) { return getFuncImpl(f, FuncTag<func>{}); }

最后:

template <Base base, Func func>
decltype(auto) getElem(Foo& foo) { return getFunc<func>(getBase<base>(foo)); }