【问题标题】:Template deduction complaints ambiguous candidates模板扣除投诉模棱两可的候选人
【发布时间】:2018-11-13 05:58:32
【问题描述】:

我打算实现我的“稀疏向量”和“向量”类的乘法运算符。下面的简化代码演示显示了我的问题

Vector.hpp

中的Vector
#pragma once

template <typename T>
class Vector 
{
public:
    Vector() {}

    template <typename Scalar>
    friend Vector operator*(const Scalar &a, const Vector &rhs)     // #1
    {
        return Vector();
    }
};

SpVec.hpp

中的稀疏向量
#pragma once
#include "Vector.hpp"

template <typename T>
class SpVec 
{
public:
    SpVec() {}

    template <typename U>
    inline friend double operator*(const SpVec &spv, const Vector<U> &v)   // #2
    {
        return 0.0;
    }
};

ma​​in.cpp中的测试代码:

#include "Vector.hpp"
#include "SpVec.hpp"


#include <iostream>

int main() 
{
    Vector<double> v;

    SpVec<double> spv;

    std::cout << spv * v;
    return 0;
}

我用

构建测试程序
g++ main.cpp -o test

这给出了模棱两可的模板推导错误

main.cpp: In function ‘int main()’:
main.cpp:13:26: error: ambiguous overload for ‘operator*’ (operand types are ‘SpVec<double>’ and ‘Vector<double>’)
        std::cout << spv * v;
                    ~~~~^~~
In file included from main.cpp:2:0:
SpVec.hpp:12:26: note: candidate: double operator*(const SpVec<T>&, const Vector<U>&) [with U = double; T = double]
    inline friend double operator*(const SpVec &spv, const Vector<U> &v)   // #2
                        ^~~~~~~~
In file included from main.cpp:1:0:
Vector.hpp:10:19: note: candidate: Vector<T> operator*(const Scalar&, const Vector<T>&) [with Scalar = SpVec<double>; T = double]
    friend Vector operator*(const Scalar &a, const Vector &rhs)     // #1

我希望#2 方法定义更接近我的要求。

请帮助我了解模棱两可的错误是如何产生的以及如何解决问题。

【问题讨论】:

    标签: c++ templates metaprogramming ambiguous


    【解决方案1】:

    我想出了另一个想法,即先前的类型信息Scalar 可以与c++11 标准库结构std::enable_if 启用的SFAINE feature 一起使用。

    代码:

    Vector.hpp

    #pragma once
    
    #include <iostream>
    #include <type_traits>
    
    template <typename T>
    class Vector
    {
    public:
        Vector() {}
    
        template <typename Scalar>
        typename std::enable_if<std::is_arithmetic<Scalar>::value, Vector<T>>::type
        operator*(const Scalar &rhs) const// #1
        {
            std::cout << "Vector * Scalar called." << std::endl;
            return Vector();
        }
    
        template <typename Scalar>
        inline friend typename std::enable_if<std::is_arithmetic<Scalar>::value, Vector<T>>::type
        operator*(const Scalar &lhs, const Vector &rhs)
        {
            std::cout << "Scalar * Vector called." << std::endl;
            return Vector();
        }
    };
    

    SpVec.hpp

    #pragma once
    #include "Vector.hpp"
    
    #include <iostream>
    
    template <typename T>
    class SpVec
    {
    public:
        SpVec() {}
    
        template <typename U>
        inline double operator*(const Vector<U> &rhs) const // #2 as member function
        {
            std::cout << "SpVec * Vector called" << std::endl;
            return 0.0;
        }
    
        template <typename U>
        inline friend double operator*(const Vector<U> &lhs, const SpVec &rhs)
        {
            std::cout << "Vector * SpVec called" << std::endl;
            return 0.0;
        }
    };
    

    ma​​in.cpp

    #include "SpVec.hpp"
    #include "Vector.hpp"
    
    #include <iostream>
    
    int main()
    {
        Vector<double> v;
        SpVec<double> spv;
    
        double a = spv * v;
        a = v * spv;
    
        Vector<double> vt;
        vt = v * 2.0;
        vt = 2.0 * v;
    
        return 0;
    }
    

    使用c++11构建程序

    g++ -std=c++11 main.cpp -o test
    

    结果:

    SpVec * Vector called.
    Vector * SpVec called.
    Vector * Scalar called.
    Scalar * Vector called.
    

    【讨论】:

    • 这也不错!
    【解决方案2】:

    operator* 的参数是 SpVec&lt;double&gt;Vector&lt;double&gt;。可以解决

    operator*(const Scalar &amp;a, const Vector &amp;rhs)scalarSpVec&lt;double&gt;rhsVector&lt;double&gt;

    也可以解析为

    operator*(const SpVec &amp;spv, const Vector&lt;U&gt; &amp;v) 与 spv 为 SpVec&lt;double&gt;Udouble

    解决此问题的一种方法是将Vector::operator* 转为非好友功能。

    Vector operator*(const Scalar &a)     // #1
    {
        //The other argument here will be accessed using this pointer.
        return Vector();
    }
    

    你可以称它为

    int main() 
    {
       Vector<double> v;
       SpVec<double> spv;
       std::cout << spv * v; // will call #2
       v * spv;              //will call #1
       return 0;
    }
    

    【讨论】:

    • 你打败了我!
    • 谢谢。这个建议确实有效。对于更深入的讨论,是否有可能使这两种情况都起作用,因为用户不需要关心操作数的优先级?
    • 不错的答案。但是,这仅是因为您使用了参数的顺序(使用假定为可交换的运算符)。
    • @Rubin 我认为您正在寻找的术语是可交换的,而不是优先级。交换性质表示a*b 等于b*a。优先级用于决定几个运算符的执行顺序:a + b * c 其中乘法具有更高的优先级,将在加法之前计算。这里还有一个关于非模板案例中交换属性问题的链接:stackoverflow.com/questions/3764604/…
    • @Philipp 感谢您的更正。 commutative property 确实是我需要的。
    猜你喜欢
    • 2021-12-19
    • 2023-03-21
    • 2010-11-13
    • 1970-01-01
    • 1970-01-01
    • 2018-05-10
    • 2015-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多