c++ - 编译时选择特定方法,伪装成操作符
问题描述
我有一堂像这样的课:
struct Base
{
void aa(int n) const {
std::cout << "aa() " << field*n << std::endl;
}
void bb(int n) const {
std::cout << "bb() " << field*n*2 << std::endl;
}
int field = 2;
};
我希望能够在编译时选择两个实现之一,aa()
或者bb()
通过调用运算符方法。就像是:
Base data;
Magic obj(data);
obj.as_AA() * 33; // should call data.aa(33)
obj.as_BB() * 44; // should call data.bb(44)
data
不得重复。而aa()
vs的选择bb()
必须在编译时解决。
我有一个使用向下转换的解决方案,其行为在理论上是未定义的(我认为)。它构建(使用 g++ 和 clang++)并完美运行,但仍然......
struct AA : public Base
{
void operator*(int n) const {
return Base::aa(n);
}
};
struct BB : public Base
{
void operator*(int n) const {
return Base::bb(n);
}
};
struct Chooser
{
Base obj;
template<typename WHICH> // will be either AA or BB
const WHICH& as() const {
return static_cast<const WHICH&>( obj ); // downcasting
}
};
在main.cpp
:
Chooser ch;
ch.as<AA>() * 5; // prints "aa() 10"
ch.as<BB>() * 7; // prints "bb() 28"
我的解决方案有多不可靠?(因为技术上未定义的向下转换)
你看到替代品了吗?
谢谢
ps:当然我可以简单地使用
Base data;
data.aa(33);
data.bb(44);
但我真的想通过相同的名称访问不同的实现,即 operator*
我也可以使用模板化的 operator* inBase
并具有显式的模板特化,但这会迫使我使用丑陋的语法,这会使运算符的用途无效:
struct Base {
\\...
template<int N> void operator*(int n) const;
};
template<> void Base::operator*<1>(int n) const {
aa(n);
}
这需要:
Base data;
data.operator*<1>(44); // ugly
解决方案
你可以这样写Magic
类:
struct Magic {
Magic(Base &b) : b(b) {}
Base &b;
struct AA {
Base &b;
void operator*(int n) const {
return b.aa(n);
}
};
struct BB {
Base &b;
void operator*(int n) const {
return b.bb(n);
}
};
AA as_AA() { return AA{b}; }
BB as_BB() { return BB{b}; }
};
这通过使用组合来避免任何继承。此外,没有data
对象的副本,因为仅对其进行了引用。
现在您可以准确地使用您想要的调用语法,并且它具有正确的行为:
Base data;
Magic obj(data);
obj.as_AA() * 33; // calls data.aa(33) -- prints 66
obj.as_BB() * 44; // calls data.bb(44) -- prints 176
这是一个演示。