具有多种返回类型的函数

时间:2016-08-30 10:11:03

标签: c++ c++11

我有两个枚举,基本上(在运行时)确定要做什么。 'mapping'看起来像

GetNameService

问题是,fragmentBstruct 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()); a以及bC的返回类型都是不同的类型(一些非常庞大的模板类型。)

使用开关或ifs映射两个枚举非常难看,需要付出很多努力,所以我想知道,如果我能以某种方式写出这样的东西:

x()

编辑1: 我无法编辑课程y()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))); CA。我也无法编辑CBCC的类/返回类型。所有这些类型都来自外部图书馆。

5 个答案:

答案 0 :(得分:4)

您可以使用延续传递方式。

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>

答案 1 :(得分:1)

简而言之:你做不到。函数的返回类型必须是众所周知的编译时间。实际上,尝试在具有auto返回类型的函数内返回不同类型,即使您使用模板,它也会给您编译错误。

您的解决方案可能包含多头的使用。

答案 2 :(得分:1)

另一种解决方案是使用特征:

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

答案 3 :(得分:0)

函数应始终返回具有相同数据类型的值(在编译时确定)。你可以做的是使用某种形式的变体,例如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; }
};

答案 4 :(得分:0)

如果在编译时已知参数,则可以使用标签分派:

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