"Dynamic"没有模板或向下投射的参数类型(?
"Dynamic" type of argument without template or downcast (?)
我有我的fancyFunction
,它需要一组实现接口A
的元素。该函数根据通过接口读取的属性对这些元素进行复杂的分析A
。在此分析过程中,它将调用将元素作为参数的Consumer c
的方法。
该Consumer
旨在采用与A
完全无关的特定类型的参数。
你可以想象A
是图形中边缘的抽象。图形以fancyFunction
进行分析,例如,每次函数"越过"一条边时,它都会将该边发送到一个Consumer
,该打印存储在边缘中的附加信息,这些信息与它是一条边无关。
下面给出的代码当然不会用类型语言(特别是C++)编译,但省略类型(Matlab,Python),代码就可以工作。
为了使它以键入的语言(特别是C++)工作,我看到了两个选项:
-
将函数声明为
template <class CONSUMER> void fancyFunction(A[] setOfAs, CONSUMER c){ ... }
-
声明
operation1
和operation2
来获取最通用的对象,然后在实现中执行向下转换。
在这种情况下,您建议怎么做? (据我所知,访客模式不是一种选择。
完整的代码大纲(我有一段时间没有使用C++,所以如果有轻微的语法错误,请原谅。
void fancyFunction(A[] setOfAs, Consumer* c){
// do fancy analysis of setOfAs by properties
// read through interface A
double x = setOfAs[i]->getX();
// call functions in c with arguments of setOfAs[j]
...
c->operationX(setOfAs[i]);
...
c->operationY(setOfAs[j]);
...
}
class A{
virtual double getX();
}
class Consumer{
virtual void operationX(??? x); // whoops, what type do we expect?
virtual void operationY(??? y); // whoops, what type do we expect?
}
class Consumer1{
void operationX(Obj1 x){ ... } // whoops, override with different type
void operationY(Obj1 y){ ... } // whoops, override with different type
}
class Consumer2{
void operationX(Obj2 x){ ... } // whoops, override with different type
void operationY(Obj2 y){ ... } // whoops, override with different type
}
class Obj1 : public A {};
class Obj2 : public A {};
void test(){
Obj1 o1[];
Obj2 o2[];
Callback1 c1;
Callback2 c2;
fancyFunction(o1, &c1);
fancyFunction(o2, &c2);
}
您正在寻找的解决方案称为访客模式。
您不想在花哨的函数中手动转换对象 A 的每个实例,因为这是维护的噩梦和清晰的代码气味。
另一方面,如果每个对象都自动处理自己的强制转换怎么办? 这就是访客模式。
首先在基类 (A) 中定义一个新的 "Visit" 函数,将 Consumer 作为其唯一参数:
class A
{
public:
virtual void Visit(Consumer& consumer) = 0;
}
然后为每个继承的类实现此函数,因此:
class B : public A
{
public:
void Visit(Consumer& consumer)
{
consumer.DoOperation(this); // 'this' utomatically resolves to type B*
}
}
现在,每个派生类型通过将"this"指针传递给提供的使用者实例来处理调用相应的操作重载。 "this"指针会自动解释为最具体的类型。
回顾原始示例代码,似乎每个使用者都提供多个操作,并且只处理一种类型。 此模式可能需要稍微更改此范例:为每个操作创建一个使用者,其中每个使用者为每个可能的继承类型提供重载。
class ConsumerX
{
public:
void DoOperation(A* a) { /* ERROR! This is a base type. If this function is called, you probably need to implement another overload. */ }
void DoOperation(B* b) { /* Much better */ }
}
class ConsumerY
{
public:
void DoOperation(A* a) { /* ERROR! This is a base type. If this function is called, you probably need to implement another overload. */ }
void DoOperation(B* b) { /* Much better */ }
}
然后,您的实现循环如下所示:
ConsumerX consumerX; // Does Operation X for every type
ConsumerY consumerY; // Does Operation Y for every type
for(int x = 0; x < numElements, x++)
{
auto element = setOfAs[x];
element.Visit(consumerX); //Do operation X
element.Visit(consumerY); //Do operation Y
}
显然是合适的模板。我什至会质疑为什么你的fancyFunction
坚持基础类A
.它应该只需要一个开始和结束迭代器。我也不会打扰消费者。也让它变得灵活,只需使用任何功能。
事实上,我什至不会写fancyFunction
.它已经存在:
std::for_each(o1.begin(), o1.end(),
[c1](Obj1 o) { double x = o.getX(); c1.operationX(o); c1.operationY(o); }
);
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型