【问题标题】:Value category of const int variable captured by lambdalambda 捕获的 const int 变量的值类别
【发布时间】:2017-04-29 17:38:51
【问题描述】:

我一直试图了解何时以及何时不具有捕获默认 odr 的 lambda 使用具有在其周围范围内定义的自动存储持续时间的变量(由 this answer 提示)。在探索这个问题时,我遇到了一点好奇。 GCC 和 Clang 似乎对以下代码中 id-expression n 的值类别存在分歧:

template <typename T> void assert_is_lvalue(const T&) {}
template <typename T> void assert_is_lvalue(const T&&) = delete;

int main() {
    const int n = 0;
    [=] { assert_is_lvalue(n); };
}

Clang 成功编译代码,而 GCC 没有 (error: use of deleted function)。哪一个是正确的?或者这是未指定或实现定义的东西?

绑定对对象的引用应该是 odr-use 它,这通过删除 lambda 的默认捕获并观察到两个编译器抱怨 n 不能在没有捕获默认的情况下被隐式捕获来确认。

将 lambda 标记为 mutable 对编译器的输出没有明显影响。

【问题讨论】:

  • GCC 确实 not complain 关于 n 如果我们删除默认捕获不会被捕获。似乎只是一个明显的错误。
  • @Columbo 很好发现,我错过了。不过,我不认为这是一个错误。我认为 gcc 将n 视为常量表达式,它可能是allowed to do
  • 是的,众所周知,GCC 会在 lambda 超早期错误地进行常量折叠。
  • 它看起来像是编译器试图使 n constexpr 进行的过早优化,然后在您的示例中推导出右值并失败。绝对是 GCC 错误
  • 适用于 gcc-8.1

标签: c++ lambda language-lawyer c++17 value-categories


【解决方案1】:

原来 gcc 的行为已经从 gcc-7.5 向上改变了!我使用下面的代码来查看如何在 lambda 中捕获 n 以及匹配哪个模板。

#include <iostream>
#include <string>
#include <typeinfo>
#include <type_traits>
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
constexpr std::string_view type_name()
{
    using namespace std;

    #ifdef __clang__
        string_view p = __PRETTY_FUNCTION__;
        return string_view(p.data() + 34, p.size() - 34 - 1);
    #elif defined(__GNUC__)
        string_view p = __PRETTY_FUNCTION__;

        #if __cplusplus < 201402
            return string_view(p.data() + 36, p.size() - 36 - 1);
        #else
            return string_view(p.data() + 49, p.find(';', 49) - 49);
        #endif
    #elif defined(_MSC_VER)
        string_view p = __FUNCSIG__;
        return string_view(p.data() + 84, p.size() - 84 - 7);
    #endif
}

template <typename T> 
void assert_is_lvalue(const T& param) 
{
    std::cout << "  T is " << type_name<T>() 
              << "  param is " << type_name<decltype(param)>() << '\n';
}

//template <typename T> void assert_is_lvalue(const T&&) = delete;
template <typename T> 
void assert_is_lvalue(const T&& param)
{
    std::cout << "  T is " << type_name<T>() 
              << "  param is " << type_name<decltype(param)>() << '\n';
}

int main() 
{
    const int n = 0;
    [=] { 
        std::cout << "  n is " << type_name<decltype(n)>() << '\n';
        assert_is_lvalue(n); 
    }();

    return 0;
}

结果如下:

gcc-7.5

n is const int
T is int  param is const int&&

gcc-8.1

n is const int
T is int  param is const int&

您可以使用代码here

【讨论】:

    猜你喜欢
    • 2021-06-02
    • 2015-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2013-11-26
    相关资源
    最近更新 更多