为什么 lambda 自动和参数选择常量重载?
Why does lambda auto& parameter choose const overload?
我正在尝试实现包裹任意类型和静音的类。要访问包装数据,需要将功能对象作为locked
方法的参数传递。然后,包装器类将把包装的数据作为参数传递给此函数对象。
我希望我的包装班与const&非企业,所以我尝试了以下
#include <mutex>
#include <string>
template<typename T, typename Mutex = std::mutex>
class Mutexed
{
private:
T m_data;
mutable Mutex m_mutex;
public:
using type = T;
using mutex_type = Mutex;
public:
explicit Mutexed() = default;
template<typename... Args>
explicit Mutexed(Args&&... args)
: m_data{std::forward<Args>(args)...}
{}
template<typename F>
auto locked(F&& f) -> decltype(std::forward<F>(f)(m_data)) {
std::lock_guard<Mutex> lock(m_mutex);
return std::forward<F>(f)(m_data);
}
template<typename F>
auto locked(F&& f) const -> decltype(std::forward<F>(f)(m_data)) {
std::lock_guard<Mutex> lock(m_mutex);
return std::forward<F>(f)(m_data);
}
};
int main()
{
Mutexed<std::string> str{"Foo"};
str.locked([](auto &s) { /* this doesn't compile */
s = "Bar";
});
str.locked([](std::string& s) { /* this compiles fine */
s = "Baz";
});
return 0;
}
使用通用lambda的第一个locked
呼叫未能与以下错误一起编译
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp: In instantiation of ‘main()::<lambda(auto:1&)> [with auto:1 = const std::__cxx11::basic_string<char>]’:
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:30:60: required by substitution of ‘template<class F> decltype (forward<F>(f)(((const Mutexed<T, Mutex>*)this)->Mutexed<T, Mutex>::m_data)) Mutexed<T, Mutex>::locked(F&&) const [with F = main()::<lambda(auto:1&)>]’
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:42:6: required from here
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:41:11: error: passing ‘const std::__cxx11::basic_string<char>’ as ‘this’ argument discards qualifiers [-fpermissive]
s = "Bar";
^
In file included from /usr/include/c++/5/string:52:0,
from /usr/include/c++/5/stdexcept:39,
from /usr/include/c++/5/array:38,
from /usr/include/c++/5/tuple:39,
from /usr/include/c++/5/mutex:38,
from /home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:1:
/usr/include/c++/5/bits/basic_string.h:558:7: note: in call to ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
operator=(const _CharT* __s)
^
但是第二个带有std::string&
参数的调用很好。
为什么?是否有一种方法可以使其在使用通用lambda时按预期工作?
这与sfinae nofryly callables发生的事情从根本上是一个问题。有关更多参考,请查看P0826。
问题是,当您致电时:
str.locked([](auto &s) { s = "Bar"; });
我们有locked
的两个超载,我们必须同时尝试两者。非const
过载正常。但是const
One&ndash;即使不会通过超载分辨率选择它,否则仍然必须实例化(这是一个通用的lambda,因此要弄清楚您可能必须实例化的decltype(std::forward<F>(f)(m_data))
),并且实例化在lambda主体内失败。身体不在直接的背景下,因此不是替代失败&ndash;这是一个困难的错误。
当您致电时:
str.locked([](std::string& s) { s = "Bar"; });
在整个超负荷分辨率的过程中,我们根本不需要查看身体。我们可以在呼叫网站上拒绝(因为您不能将const string
传递到string&
中)。
今天的语言中没有真正解决这个问题的解决方案;基本上,您必须在lambda上添加约束,以确保实例化故障发生在替换的直接背景下而不是在体内。类似:
str.locked([](auto &s) -> void {
s = "Bar";
});
请注意,我们不需要使此Sfinae友好 - 我们只需要确保我们可以在不实例化身体的情况下确定返回类型。
一种更彻底的语言解决方案是允许"推论this
"(请参阅有关此特定问题的论文中的一部分)。但这不会在C 20中。
- #定义c-预处理器常量..我做错了什么
- 如何使用默认参数等选择模板专业化
- 用C++中的一个变量定义一个常量
- 什么时候在C++中返回常量引用是个好主意
- 代理对象的常量正确性
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 如何(从固定列表中)选择一个数字序列,该序列将与目标数字相加
- 通过多个头文件使用常量变量
- 选择要调用的构造函数
- 在cuda线程之间共享大量常量数据
- C++选择排序算法中的逻辑错误
- 当通过常量和不是字符串的最佳选择时,是否有任何情况?
- 为什么 lambda 自动和参数选择常量重载?
- 常量表达式来自重载函数的选择
- 强制编译器选择常量运算符重载
- C++为常量选择类型
- 在参考 (T&) 和常量指针 (T* 常量) 之间进行选择
- 为什么选择非常量版本而不是类的常量版本
- c++非常量-常量重载方法选择
- 在c++中,当常量也可以工作时,编译器为什么选择非常量函数