C++ 具有多个返回类型的函数
我有两个枚举,它们基本上决定(在运行时)要做什么。“映射”看起来像C++ 具有多个返回类型的函数,c++,c++11,C++,C++11,我有两个枚举,它们基本上决定(在运行时)要做什么。“映射”看起来像 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 =
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());
问题是,a
、b
和C
,以及x()
和y()
的返回类型都是不同的类型(一些非常庞大的模板类型)
使用开关或ifs映射这两个枚举非常难看,需要付出很多努力,因此我想知道,我是否可以这样写:
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)));
structfoo{class CA;class CB;class CC;CA a;CB b;CC c;};
枚举基{A,B,C,};
枚举函数{X,Y,};
模板自动应用程序函数(函数f,T)
{
开关(f)
{
案例X:返回t.X();
案例Y:返回t.Y();
}
}
自动获取基本(基本b,基本f)
{
开关(b)
{
案例A:返回f.A;
案例B:返回f.B;
案例C:返回f.C;
}
}
函数f;
碱基b;
富富,;
使用(applyFunc(f,getBase(b,foo));
编辑1:
我无法编辑类
CA
、CB
和CC
。我也不能编辑x()
和y()
的类/返回类型。所有这些类型都来自外部库。简而言之:您不能。函数的返回类型必须在编译时已知。事实上,尝试在函数中使用auto
返回类型返回不同的类型,即使使用模板,也会导致编译器错误
您的解决方案可能包括使用polymorfism。函数应始终返回在编译时确定的具有相同数据类型的值。例如,您可以使用某种形式的变体,或创建自己的变体类型:
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; }
};
类结果\u处理程序{
公众:
虚~result_handler(){}
虚空句柄_字符串(字符串s)=0;
虚空句柄_int(int i)=0;
虚空句柄_double(double d)=0;
};
空f(整数x,结果处理程序*h){
开关(x){
案例0:
h->handle_字符串(f1());
打破
案例1:
h->handle_int(f2());
打破
案例2:
h->handle_double(f3());
打破
}
}
类my_result_处理程序:public result_处理程序{
公众:
虚空句柄_string(string s){cout您可以使用延续传递样式
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);
}
}
或者类似的(可能有打字错误)
std::variant
(或boost)可用于将延续移动到返回值之后
auto b = getBase(b);
auto r = visit( b, [&](auto&& b){ return applyFunc( f, b ); } );
visit( r, [](auto&& r){ use(r); } );
其中,上述每种方法都返回一个变量,而不是继续执行。如果编译时参数已知,则可以使用标记分派:
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>{}); }
模板基标签{};
decltype(auto)getBaseImpl(Foo&Foo,BaseTag){return Foo.a;}
decltype(auto)getBaseImpl(Foo&Foo,BaseTag){return Foo.b;}
decltype(auto)getBaseImpl(Foo&Foo,BaseTag){return Foo.c;}
模板
decltype(auto)getBase(Foo&Foo){return getBaseImpl(Foo,BaseTag{});}
模板FuncTag{};
模板
decltype(auto)getFuncImpl(T&T,FuncTag){return T.x();}
模板
decltype(auto)getFuncImpl(T&T,FuncTag){return T.y();}
模板
decltype(auto)getFunc(T&T){return getFuncImpl(f,FuncTag{});}
最后:
template <Base base, Func func>
decltype(auto) getElem(Foo& foo) { return getFunc<func>(getBase<base>(foo)); }
模板
decltype(auto)getElem(Foo&Foo){return getFunc(getBase(Foo));}
另一个解决方案是使用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);
}
struct Foo{class CA{};class CB{};class CC{};CA a;CB b;CC c;};
枚举基{A,B,C,};
枚举函数{X,Y,};
模板结构GetBaseTraits;
模板结构GetBaseTraits{using type=Foo::CA;};
模板结构GetBaseTraits{using type=Foo::CB;};
模板结构GetBaseTraits{using type=Foo::CC;};
模板
typename-GetBaseTraits::type-getBase(Foo-f);
模板typename GetBaseTraits::type getBase(Foo f){return f.a;}
模板typename GetBaseTraits::type getBase(Foo f){return f.b;}
模板typename GetBaseTraits::type getBase(Foo f){return f.c;}
int main(){
富福{};
Foo::CA=getBase(f);
Foo::CB CB=getBase(f);
Foo::CC=getBase(f);
}
哼哼……想成为……你想实现什么?你想用这种设计实现什么真正的问题?我想到的第一个想法是你试图重新实现virtual
inheritance你做不到。在运行时决定使用哪个模板总是会在每次使用时导致丑陋的切换,而你无法将其分解为函数。其次是多态性,该函数返回一个实际指向正确类(工厂模式)的唯一\u ptr
。@nwp在某些情况下,可以编写编译器生成的跳转表来替换开关。或链ifs。或者…这只是有时的改进,但使用“始终”是完全错误的。@Yakk您是对的,您可以使用if-chains或goto实现切换,并说您没有使用switch。从技术上讲,这是错误的。目的是避免每次使用都必须编写映射代码,而不是避免使用switch关键字。@nwp或我可以从两个平行列表中生成mappimg如果我控制了枚举,枚举的值就是一个足够的列表。我的观点是你提出了一个过于强烈的主张,我们不知道什么标准可以使解决方案“更好”比OP的switch语句更重要。此外,OP正在询问如何使两个独立的switch语句工作,这也是非常可行的。但是当getBase
的模板参数是一个运行时变量时,这不起作用,对吗?@Uroc327对。我不明白这是一个要求。如果是这样,我会在一段时间内删除答案。
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);
}