你能在模板参数中使用decltype吗?

时间:2014-04-15 23:17:45

标签: c++ templates c++11 decltype trailing-return-type

我试图重载一个乘法运算符但是不想输出多个重载函数来考虑乘以int和float,int和double,float和int等等...我希望写一个重载运算符考虑乘法与浮点数,整数和双精度的所有组合,并获得正确的返回类型。我收到错误,说没有运算符发现哪个采用'Widget :: Widget'类型的右手操作数(或者没有可接受的转换)。我想这是因为我使用decltype来设置返回对象的模板类型Widget。如果返回不是模板对象,则使用尾随返回类型。

以下是我尝试重载运算符的示例:

template<typename T1, typename T2>
auto
operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge) -> Widget<decltype(aWidge.x*bWidge.x)>
{
    Widget<decltype(aWidge.x*bWidge.x)> result;

    //do stuff needed when multiplying two Widgets

    return result;
}

template<typename T>
Widget<T>& Widget<T>::operator=(const Widget<T>& aWidget)
{
    x = aWidget.x;

    return *this;
}

以下是模板类

的示例
template<typename T> class Widget
{
    private:
        T x;
    public:
        Widget();
        ~Widget();
        void SetX(T value);
        Widget<T>& operator=(const Widget<T>& aWidget);
}

示例Main.cpp

int main()
{
    Widget<int> aWidge;
    Widget<float> bWidge;
    Widget<float> cWidge;

    aWidge.SetX(2);
    bWidge.SetX(2.0);

    cWidge = aWidge*bWidge; //this should give a float return type
}

2 个答案:

答案 0 :(得分:4)

仔细阅读错误信息,问题很明显:

candidate template ignored: substitution failure [with T1 = int, T2 = float]: 'x' is
      a private member of 'Widget<int>'

非成员二进制文件operator*正试图在其声明(和定义)中访问私有成员x。由于你有一个setter函数,一个简单的解决方案是通过这个函数定义一个getter并且只访问成员x

template<typename T> class Widget
{
    private:
        T x;

    public:
        Widget() {}
        ~Widget() {}
        void SetX(T value) {}
        T& GetX() { return x; }
        const T& GetX() const { return x; }
        Widget<T>& operator=(const Widget<T>& aWidget);
};

template<typename T1, typename T2>
auto
operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge)
-> Widget<decltype(aWidge.GetX()*bWidge.GetX())>
{
    Widget<decltype(aWidge.GetX()*bWidge.GetX())> result;
    //...
    return result;
}

另一个选择是让operator*成为朋友:

template<typename T> class Widget
{
    private:
        T x;

    template<typename T1, typename T2>
    friend auto
    operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge)
    -> Widget<decltype(aWidge.x*bWidge.x)>;

    public:
        Widget() {}
        ~Widget() {}
        void SetX(T value) {}
        Widget<T>& operator=(const Widget<T>& aWidget);
};

template<typename T1, typename T2>
auto
operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge)
-> Widget<decltype(aWidge.x*bWidge.x)>
{
    Widget<decltype(aWidge.x*bWidge.x)> result;

    return result;
}

或者,使其成为会员功能(感谢WhozCraig)。

您可能还需要

typename std::decay<decltype(aWidge.x*bWidge.x)>::type

而非decltype(aWidge.x*bWidge.x)

其他选项

typename std::decay<decltype(std::declval<T1>()*std::declval<T2>())>::type

完全绕过上一个问题(感谢Adam),或者只是

typename std::common_type<T1, T2>::type

应该适合这个目的,可以说是最简单的形式。

答案 1 :(得分:3)

Visual Studio 2012

不要介意草率的代码。对于没有正确编译的代码,这是一个快速修复(永远不要解决自动decltype问题)。

template<typename T>
class Widget
{
public:
    T x;
public:
    Widget()
        : x(666)
    {}

    ~Widget() {}

    void SetX(T value)
    {
        x = value;
    }

    Widget<T>& operator=(const Widget<T>& aWidget)
    {
        x = aWidget.x;

        return *this;
    }
};


template<typename T1, typename T2>
auto operator*(const Widget<T1>& aWidge, const Widget<T2>& bWidge) -> Widget<typename std::remove_const<decltype(aWidge.x*bWidge.x)>::type>
{
    Widget<typename std::remove_const<decltype(aWidge.x*bWidge.x)>::type> result;

    result.x = aWidge.x * bWidge.x;

    return result;
}



int main ()
{
    Widget<int> aWidge;
    Widget<float> bWidge;
    Widget<float> cWidge;

    aWidge.SetX(2);
    bWidge.SetX(2.0);

    cWidge = aWidge*bWidge; //this should give a float return type
}