我一直在努力研究如何制作一个最小的可验证的例子,但我想不出怎么做。我有两个预期类型,我试图加在一起。期望的可以是数据类型,例如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;
}
答案 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]。