自动添加 const 的 std::function 参数类型的类型推导
type deduction for std::function argument types with auto adds const
我有一个结构,它有一个名为 call
的方法,它有一个 const 重载。唯一的参数是 std::function
,它根据重载接受 int 引用或 const int 引用。
genericCall
方法做完全相同的事情,但使用模板参数而不是 std::function
作为类型。
struct SomeStruct {
int someMember = 666;
void call(std::function<void(int&)> f) & {
f(someMember);
std::cout << "call: non const\n";
}
void call(std::function<void(const int&)> f) const& {
f(someMember);
std::cout << "call: const\n";
}
template <typename Functor>
void genericCall(Functor f) & {
f(someMember);
std::cout << "genericCall: non const\n";
}
template <typename Functor>
void genericCall(Functor f) const& {
f(someMember);
std::cout << "genericCall: const\n";
}
};
当我现在创建此结构并使用 lambda 和 auto &
作为参数调用 call
时,尽管对象不是 const,std::function
总是推导出 const int &
。
另一方面,genericCall
在 lamdba 中将参数正确推导为 int &
。
SomeStruct some;
some.call([](auto& i) {
i++; // ?? why does auto deduce it as const int & ??
});
some.genericCall([](auto& i) {
i++; // auto deduces it correctly as int &
});
我完全不知道为什么 auto
在这两种情况下表现不同,或者为什么 std::function
似乎更喜欢在这里使参数为 const。尽管调用了正确的方法,但这会导致编译错误。当我将参数从 auto &
更改为 int &
时,一切正常。
some.call([](int& i) {
i++;
});
当我对结构的 const 版本执行相同的调用时,一切都按预期推导。 call
和 genericCall
都在这里推导出一个 const int &
。
const SomeStruct constSome;
constSome.call([](auto& i) {
// auto deduces correctly const int & and therefore it should
// not compile
i++;
});
constSome.genericCall([](auto& i) {
// auto deduces correctly const int & and therefore it should
// not compile
i++;
});
如果有人能对此有所启发,我将不胜感激!
对于那些想深入研究的更好奇的人,拉取请求中出现了这个问题:https://github.com/eclipse-iceoryx/iceoryx/pull/1324 在为 expected
实现实现功能接口时。
问题是通用 lambda(自动参数)等同于 operator()
模板化的可调用对象。这意味着 lambda 参数的实际类型不包含在 lambda 中,并且仅在调用 lambda 时推导。
但是在您的情况下,通过具有特定的 std::function 参数,您可以在调用 lambda 之前强制转换为具体类型,因此无法从任何内容中推断出自动类型。 non-template 上下文中没有 SFINAE。
没有特定的参数类型,您的 call
都是有效的重载。实际上任何可以匹配 [](auto&)
的 std::function 都是有效的。现在唯一的规则可能是最多 cv-qualified 过载获胜。您可以尝试使用 volatile float&
,您会发现它仍然会选择那个。一旦选择此重载,尝试调用时编译将失败。
问题是尝试确定您的 lambda 是否为 Callable 与 const int &
returning void
是一个硬错误,需要确定是否你可以构造一个 std::function<void(const int&)>
.
您需要实例化 lambda 的主体以确定 return 类型。这不在替换模板参数的直接上下文中,所以它不是 SFINAE。
这是一个 equivalent error 实例化特征。
正如@aschepler 在评论中指出的那样,specifying a return type 消除了实例化 lambda 主体的需要。
我有一个结构,它有一个名为 call
的方法,它有一个 const 重载。唯一的参数是 std::function
,它根据重载接受 int 引用或 const int 引用。
genericCall
方法做完全相同的事情,但使用模板参数而不是 std::function
作为类型。
struct SomeStruct {
int someMember = 666;
void call(std::function<void(int&)> f) & {
f(someMember);
std::cout << "call: non const\n";
}
void call(std::function<void(const int&)> f) const& {
f(someMember);
std::cout << "call: const\n";
}
template <typename Functor>
void genericCall(Functor f) & {
f(someMember);
std::cout << "genericCall: non const\n";
}
template <typename Functor>
void genericCall(Functor f) const& {
f(someMember);
std::cout << "genericCall: const\n";
}
};
当我现在创建此结构并使用 lambda 和 auto &
作为参数调用 call
时,尽管对象不是 const,std::function
总是推导出 const int &
。
另一方面,genericCall
在 lamdba 中将参数正确推导为 int &
。
SomeStruct some;
some.call([](auto& i) {
i++; // ?? why does auto deduce it as const int & ??
});
some.genericCall([](auto& i) {
i++; // auto deduces it correctly as int &
});
我完全不知道为什么 auto
在这两种情况下表现不同,或者为什么 std::function
似乎更喜欢在这里使参数为 const。尽管调用了正确的方法,但这会导致编译错误。当我将参数从 auto &
更改为 int &
时,一切正常。
some.call([](int& i) {
i++;
});
当我对结构的 const 版本执行相同的调用时,一切都按预期推导。 call
和 genericCall
都在这里推导出一个 const int &
。
const SomeStruct constSome;
constSome.call([](auto& i) {
// auto deduces correctly const int & and therefore it should
// not compile
i++;
});
constSome.genericCall([](auto& i) {
// auto deduces correctly const int & and therefore it should
// not compile
i++;
});
如果有人能对此有所启发,我将不胜感激!
对于那些想深入研究的更好奇的人,拉取请求中出现了这个问题:https://github.com/eclipse-iceoryx/iceoryx/pull/1324 在为 expected
实现实现功能接口时。
问题是通用 lambda(自动参数)等同于 operator()
模板化的可调用对象。这意味着 lambda 参数的实际类型不包含在 lambda 中,并且仅在调用 lambda 时推导。
但是在您的情况下,通过具有特定的 std::function 参数,您可以在调用 lambda 之前强制转换为具体类型,因此无法从任何内容中推断出自动类型。 non-template 上下文中没有 SFINAE。
没有特定的参数类型,您的 call
都是有效的重载。实际上任何可以匹配 [](auto&)
的 std::function 都是有效的。现在唯一的规则可能是最多 cv-qualified 过载获胜。您可以尝试使用 volatile float&
,您会发现它仍然会选择那个。一旦选择此重载,尝试调用时编译将失败。
问题是尝试确定您的 lambda 是否为 Callable 与 const int &
returning void
是一个硬错误,需要确定是否你可以构造一个 std::function<void(const int&)>
.
您需要实例化 lambda 的主体以确定 return 类型。这不在替换模板参数的直接上下文中,所以它不是 SFINAE。
这是一个 equivalent error 实例化特征。
正如@aschepler 在评论中指出的那样,specifying a return type 消除了实例化 lambda 主体的需要。