使用模板化类型的C ++运算符重载给了我错误

时间:2017-11-18 18:56:04

标签: c++ templates operator-overloading c++17

我一直在努力研究如何制作一个最小的可验证的例子,但我想不出怎么做。我有两个预期类型,我试图加在一起。期望的可以是数据类型,例如int,double等。或者它可以是例外。理论上,我应该能够添加带有异常的数据类型,并且当我要求值时,它将能够正常运行程序。它应该返回异常而不会导致程序崩溃。

每当我尝试运行此程序时,我会收到数百行错误消息,我甚至不知道从哪里开始,我看到的一个错误是no matching function for call to 'operator+(double&,double&)我不知道这是否正确,因为我想在一起添加两个Expected,我不想自己添加类型。最后,我想添加一个Expected,并返回并预期。

我真的被困在这里,有人告诉我,我的apply功能已被错误地实施,但老实说我不明白为什么会这样。我做错了什么?

#include <stdexcept>
#include <exception>
#include <functional>
#include <variant>

template<typename T>
class Expected
{

public:
    Expected(T t) : state(t), valid(true){} 
    Expected(std::exception_ptr e) : state(e), valid(false){}   
    Expected(std::exception e) : state(std::make_exception_ptr(e)), valid(false){}  

    T value() const 
    {
        if(valid) return std::get<T>(state);
        std::rethrow_exception(std::get<std::exception_ptr>(state));
    }

    bool isValid()
    {
        if(valid) return true;
        return false;
    }

    template<typename U>
    Expected<U> apply(std::function<U(T)> f)
    {
        if(!valid) return std::get<std::exception_ptr>(state);
        try
        {
            return f(std::get<T>(state));
        }
        catch(...)
        {
            return std::current_exception();
        }
    }
private:

    std::variant<T, std::exception_ptr> state;
    bool valid;

};

template<typename T>
std::ostream& operator<< (std::ostream& o, Expected<T> e)
{
    try
    {
        o << e.value();
    }
    catch(std::exception &a)
    {
        o << a.what();
    }
    catch(...)
    {
        o << "Unexpected Error";
    }

    return o;
}

template<typename T, typename V>
auto operator+(Expected<T> t, Expected<V> v)
{
    return t.apply([&](T myT){return operator+(myT,v);});
}
template<typename T, typename V>
auto operator+(Expected<T> t, V v)
{
    return t.apply([&](T myT){return operator+(myT,v);});
}
template<typename T, typename V>
auto operator+(V v, Expected<T> t)
{
    return t.apply([&](T myT){return operator+(v,myT);});
}

int main()
{
    Expected<int> a = 1;
    Expected<int> b = 2;

    std::cout << a + b << std::endl;
}

2 个答案:

答案 0 :(得分:4)

这是第一个问题的简化示例:

int main() {
    operator+(1, 2); // error
}

您无法按名称调用内置运算符。您只能由运营商打电话给他们。而且,只需使用操作符就更容易阅读 - 所以就这样做。使用+。 (另外,在代码的一部分中,您使用的是op,而不是operator+}。

这是第二个问题的简化示例:

template <typename T>
struct X {
    template<typename U>
    U apply(std::function<U(T)> f);
};

int main() {
    X<int>{}.apply([](int ){return 2.0;}); // error
}

基本上,推断 std::function几乎总是错误的。这个lambda 不是一个std::function不是一个std::function<double(int)>。您想要做的事情,几乎总是,推断出一个任意的可调用,然后使用元函数来确定结果:

template <typename T>
struct X {
    template <typename F, typename U = std::invoke_result_t<F&, T>>
    U apply(F f);
};

答案 1 :(得分:3)

首先关闭:一般情况下,如果您收到太多错误,请通过省略一些代码来简化您的测试用例,直到您遇到代码中的一个问题(尽管可能仍会报告为多个错误) 。要么你会理解这个错误,要么你会有一个具体的问题要问。在这种情况下,这意味着从operator +的一次重载开始,没有operator <<

现在你的代码:有很多问题。首先,为什么要调用operator+ (a, b)而不是更合乎逻辑的a + b?这实际上导致了你在问题中明确提到的错误,因为没有operator+函数占用两个double:内置运算符+不是函数,不能被调用就这样。

第二个问题是U的模板参数apply无法从lambda表达式中推导出来,因为lambda表达式的类型不是std::function。这意味着您必须为其提供显式模板参数:

return t.template apply<decltype(std::declval<T>() + v)>([&](T myT){return myT + v;});

这种变化需要在operator +的所有3次重载中发生(我假设第3次重载中的未知op是一个奇怪的拼写错误,实际上也应该调用+)。 / p>

请注意在template之前需要apply关键字,因为它在依赖的上下文中使用。

请注意,Barry's answer通过完全删除std::function来提供更好的方法。

最后,您遗漏了#include <iostream>

完成所有这些更改后,您的代码[works]