1

我很困惑,经过一些代码重构后,以下代码不再起作用,因为它跳转到auto, auto案例并忽略了该案例Complex, Complex。诚然,我不太明白重载到底在做什么,但对我来说,这两个代码看起来完全一样,除了一个直接获取它的参数,而另一个具有在函数体本身中定义的参数。

Math_Node Function_Manager::add(const Math_Node& arg){
        Math_Object obj1 = arg.children_ptr()->at(0)->data();
        Math_Object obj2 = arg.children_ptr()->at(1)->data();
        if( std::holds_alternative<Complex>(obj1) ){
            std::cerr << "obj1 is complex\n";
        }
        if( std::holds_alternative<Complex>(obj2) ){
            std::cerr << "obj2 is complex\n";
        }
        return std::visit(overload{
               [](const Complex& a, const Complex& b) -> Math_Object{ 
                   std::cerr << "COMPLEX ADD_\n"; 
                   return add_(a, b); 
               }
             , [](const Matrix& a, const Matrix& b) -> Math_Object{ 
                   std::cerr << "MATRIX ADD_\n"; 
                   return add_(a, b); 
               }
             , [&arg](auto& a, auto& b) -> Math_Node{
                   std::cerr << "NOT FOUND\n"; 
                   return arg;
             }
         }, obj1, obj2);
}

代码打印

obj1 is complex
obj2 is complex
NOT FOUND

这是重构之前的工作代码:

Math_Object Function_Manager::add(const Math_Object& arg0, const Math_Object& arg1){
        return
                std::visit(
                        overload{
                                [](const Complex& a, const Complex& b) -> Math_Object{ return add_(a, b); }
                                , [](const Matrix& a, const Matrix& b) -> Math_Object{ return add_(a, b); }
                                , [](auto& a, auto& b) -> Math_Object{
                                    throw std::runtime_error(
                                            ("Unsupported arguments for add: " + to_string(a) + to_string(b)).c_str());
                                }
                        }, arg0, arg1
                          );
    }

我唯一能想到的是,它obj1并不是obj2真正想要的类型,但打印std::cerr证明它们是。那么为什么 std::visit 不能识别它,我该如何解决呢?

4

1 回答 1

3

在您的第一个示例中obj1obj2没有const资格。

在您的第二个示例中arg0,并且arg1是。

overload只是对给它的所有 lambda 的调用运算符进行重载解析(假设它是通常的实现)。


在第一个示例的重载解析中, auto&/比/auto&更适合obj1/ ,因为后者需要资格转换来添加,而/可以推导出/ ,这不需要资格转换。obj2const Complex&const Complex&constauto&auto&Complex&Complex&


在第二个示例中情况并非如此,因为arg0/ arg1are const, / 的模板参数推导将产生auto&/并且此调用和直接采用 /的调用都不需要任何转换。auto&const Complex&const Complex&const Complex&const Complex&

如果两个函数在一次调用中具有同样好的转换顺序,那么该调用通过一些额外的标准来消除歧义,其中之一是非模板函数优于模板函数。const Complex&直接采用/的重载const Complex&不是模板(因为它不是通用 lambda),因此它是首选。


要解决这个问题,只需在所有调用中使用相同的限定条件,即在最后一次调用中使用const auto&/const auto&而不是auto&/或通过传递and而不是和to 来auto&重现第二个示例的重载解析行为。std::as_const(obj1)std::as_const(obj2)obj1obj2std::visit

于 2020-02-20T17:54:20.083 回答