Plu*_*uin 5 c++ c++-concepts c++20
我有一个类,它包装不同类型的容器/视图,并且可以使用forEach成员函数调用所有元素上的函数。函数有不同的重载forEach,它们对函数类型(这是一个模板)的约束及其常量有所不同。现在我注意到,当调用具有参数的 lambda 的非常量实例时,auto&编译器错误地选择了 const 重载,从而无法编译。下面是一个说明问题的精简示例:
#include <type_traits>
#include <concepts>
#include <vector>
#include <span>
template <class, class>
struct FunctionWithSignatureOrImplicitlyConvertible : std::false_type {};
template <class Fn, class Ret, class... Args>
struct FunctionWithSignatureOrImplicitlyConvertible<Fn, Ret (Args...)>
{
static constexpr bool value = requires (Fn&& t, Args&&... args) { { t (args...) } -> std::convertible_to<Ret>; };
};
template <class Fn, class Signature>
concept functionWithSignatureOrImplicitlyConvertible = FunctionWithSignatureOrImplicitlyConvertible<Fn, Signature>::value;
template <class T>
struct Foo
{
using ValueType = typename T::value_type;
T& vec;
Foo (T& src) : vec (src) {}
template <functionWithSignatureOrImplicitlyConvertible<void (ValueType&)> Fn>
void forEach (Fn&& fn)
{
for (auto& e : vec)
fn (e);
}
template <functionWithSignatureOrImplicitlyConvertible<void (const ValueType&)> Fn>
void forEach (Fn&& fn) const
{
for (auto& e : vec)
fn (e);
}
};
int main()
{
std::vector<float> v { 1.0f, 2.0f, 3.0f, 4.0f };
std::span s (v);
Foo a (v);
Foo b (s);
a.forEach ([] (auto& e) { e *= 2.0f; }); // -> error: cannot assign to variable 'e' with const-qualified type 'const float &'
a.forEach ([] (float& e) { e *= 2.0f; });
b.forEach ([] (auto& e) { e *= 2.0f; }); // -> error: cannot assign to variable 'e' with const-qualified type 'const float &'
a.forEach ([] (float& e) { e *= 2.0f; });
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是编译器资源管理器上的实时示例。
现在我的问题是:为什么它在 的情况下选择 const 重载auto&?在这种情况下,我该怎么做才能让编译器选择非常量重载?
在重载决策期间检查概念时functionWithSignatureOrImplicitlyConvertible<(closure type), void (const ValueType&)>,它必须使用推导的 auto 参数实例化 lambda 主体,以const float确定 auto 返回类型是什么。
这不是 SFINAE 上下文,因此const float& e e *= 2.0f是一个硬错误。这是在forEach选择函数之前发生的。
如果您使其支持 SFINAE 或删除自动返回类型,它就会消失:
a.forEach ([] (auto& e) -> decltype(e*=2.0f, void()) { e *= 2.0f; });
// functionWithSignatureOrImplicitlyConvertible<(closure type), void (const ValueType&)>
// will be false
a.forEach ([] (auto& e) -> void { e *= 2.0f; });
// functionWithSignatureOrImplicitlyConvertible<(closure type), void (const ValueType&)>
// will be true, but the non-const forEach overload will be picked anyways
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
100 次 |
| 最近记录: |