Cur*_*ous 6 c++ clang decltype language-lawyer c++17
似乎 Clang (9.0.0) 或我对如何decltype()指定在标准中工作的理解有问题。参考以下代码,
#include <utility>
#include <string>
template <typename...> class WhichType;
template <typename T>
std::remove_reference_t<T>&& move_v2(T&& t) {
WhichType<std::remove_reference_t<T>&&>{};
return static_cast<std::remove_reference_t<T>&&>(t);
}
int main() {
auto x = std::string{"a"};
[v = x]() {
// move_v2(v);
// WhichType<decltype(move_v2(v))>{};
WhichType<decltype(std::move(v))>{};
}();
}
Run Code Online (Sandbox Code Playgroud)
上面的代码有编译器输出implicit instantiation of undefined template 'WhichType<std::__1::basic_string<char> &&>',而不是预期const std::__1::basic_string<char> &&在模板参数WhichType。使用move_v2或WhichType在move_v2本身似乎输出正确的事情,但。
但是,Clang 似乎也std::move(v)按照我的预期对表达式进行了重载解析https://wandbox.org/permlink/Nv7yXnCbqxjJMVvX。这让我的一些担忧消失了,但我仍然不了解decltype()lambda 内部的行为。
在这种特殊情况下,GCC 似乎没有这种不一致https://wandbox.org/permlink/5mhrOzLn5XZO8LNB。
如果我对我的理解有误decltype()或指出此错误在 clang 中出现的确切位置,有人可以纠正我吗?乍一看似乎有点吓人。在 SFINAE 或类似的东西中使用时,这可能会导致问题。
我做了一些挖掘,在我看来,答案比一些评论看起来更微妙。
\n\n首先是对之前问题的回答。引用重要部分:
\n\n\n\n\n\n
[C++11: 5.1.2/14]:如果实体是隐式捕获的并且捕获默认值是=,或者如果使用不包含&. 对于复制捕获的每个实体,在闭包类型中声明一个未命名的非静态数据成员。这些成员的声明顺序未指定。如果实体不是对对象的引用,则此类数据成员的类型是相应捕获实体的类型,否则是引用的类型。 [..]
然后是另一个问题的这个答案。再次引用:
\n\n\n\n\n5 非泛型 lambda 表达式的闭包类型具有公共\n 内联函数调用运算符 [...]当且仅当\n lambda 时,声明此函数调用运算符或\n 运算符模板
\nconst(9.3.1) -表达式\xe2\x80\x99s 参数声明子句后面没有\nmutable。
然后我将其放在一个小测试中:
\n\n#include <iostream>\nusing std::cout;\nusing std::endl;\n\nvoid foo(const std::string&) {\n cout << "void foo(const std::string&)" << endl;\n}\n\nvoid foo(std::string&) {\n cout << "void (std::string&)" << endl;\n}\n\nstruct klaf\n{\n std::string b;\n\n void bla() const\n {\n cout << std::boolalpha << std::is_const<decltype(b)>::value << endl;\n foo(b);\n }\n};\n\nint main()\n{\n klaf k;\n k.bla();\n\n std::string s;\n const std::string s2;\n auto lam = [=]() {\n cout << std::boolalpha << std::is_const<decltype(s)>::value << endl;\n foo(s);\n\n cout << std::boolalpha << std::is_const<decltype(s2)>::value << endl;\n foo(s2);\n };\n lam();\n}\nRun Code Online (Sandbox Code Playgroud)\n\n哪些输出(GCC、Clang 和 MSVC 中的输出相同):
\n\nfalse\nvoid foo(const std::string&)\nfalse\nvoid foo(const std::string&)\ntrue\nvoid foo(const std::string&)\nRun Code Online (Sandbox Code Playgroud)\n\nklaf::b是(显然)不是const,但由于该klaf::bla函数是const,所以 thenklaf::b被视为const在对 的调用中foo。
lam在wheres被类型为 的值捕获时也是如此std::string。然而,s2已被声明为const std::stringand 并延续到 lambda 中数据成员的类型。
简而言之:在 lambda 中按值捕获不会使捕获的成员本身成为const,但由于operator()lambda 是const,因此成员将在该函数中提升到const(除非 lambda 被声明为可变)。
编辑:
\n\n受到 @arnes 评论的启发,我发现 GCC 和 Clang 之间存在差异:
\n\nint main()\n{\n int i = 12;\n auto lam = [=, ic = i]() {\n cout << std::boolalpha << std::is_const<decltype(i)>::value << endl;\n cout << std::boolalpha << std::is_const<decltype(ic)>::value << endl;\n };\n lam();\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这在 Clang 中生成false false,但false true在 GCC 中生成。换句话说,带有初始值设定项的捕获const在 GCC 中变为,但在 Clang 中则不然。
| 归档时间: |
|
| 查看次数: |
188 次 |
| 最近记录: |