std :: visit无法识别类型

时间:2020-02-20 17:32:12

标签: c++ lambda generic-lambda std-variant

我很奇怪,在对代码进行了一些重构之后,下面的代码不再起作用,因为它跳转到auto, auto的情况而忽略了Complex, Complex的情况。我承认我不太了解overload确切地在做什么,但是对我来说,这两个代码看起来完全一样,除了一个例外,一个代码直接获取其参数,而另一个代码一个具有在函数主体中定义的参数。

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

我唯一能想到的是obj1obj2并不是真正想要的类型,但是打印到std::cerr的事实证明它们是正确的。那么为什么std :: visit无法如此识别它,并且我该如何解决?

1 个答案:

答案 0 :(得分:3)

在第一个示例中,obj1obj2不符合const的条件。

在第二个示例中,arg0arg1是。

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


在第一个示例中,auto& / auto&的过载分辨率比obj1 / obj2更适合const Complex& / const Complex&,因为后者需要资格转换才能添加const,而auto& / auto&可以推导为Complex& / Complex&,而无需资格转换。


在第二个示例中不是这种情况,因为arg0 / arg1const,所以auto& / auto&的模板自变量推导将产生const Complex& / const Complex&,无论此呼叫还是直接接听const Complex& / const Complex&的呼叫都不需要任何转换。

如果两个函数的调用转换顺序相同,则该调用将通过一些其他标准来消除歧义,其中之一是非模板函数优于模板函数。直接使用const Complex& / const Complex&的重载不是模板(因为它不是通用lambda),因此是首选。


要解决此问题,只需在所有通话中都具有相同的资格,即在上一个通话中使用const auto& / const auto&而不是auto& / auto&或重现第​​二个电话通过将std::as_const(obj1)std::as_const(obj2)而不是obj1obj2传递到std::visit来实现示例的重载解析行为。