【问题标题】:Can sizeof be applied inside a lambda on a variable that is not captured or is this a compiler bug?可以将 sizeof 应用于未捕获的变量的 lambda 内还是这是编译器错误?
【发布时间】:2016-11-03 16:21:13
【问题描述】:

这是found here讨论的后续。

以下代码在 gcc 和 clang (live demo) 下编译。这对于//1 行中的情况令人惊讶,因为 lambda 没有捕获任何内容。对于MCR2 的情况,lambda 返回指针本身,我们得到预期的编译时错误(// Will not compile 行)。运算符sizeof的应用与返回指针有何不同?

#include <iostream>

#define MCR1(s) \
  ([]() { return sizeof(s); })()

#define MCR2(s) \
  ([]() { return s; })()

int main() {
  auto *s= "hello world";

  auto x1 = MCR1( s ); //1
  auto y1 = MCR1( "hello world" );
//  auto x2= MCR2( s ); // Will not compile
  auto y2= MCR2( "hello world" );

  std::cout << x1  << "  " << y1  << '\n';
  std::cout // << x2 << "  " 
            << y2 << '\n';
}

编辑: 继续讨论这里是另一个例子。令人惊讶的是,标记为//2 的行现在可以在 gcc7(开发版)(live demo)下编译。这里的区别是表达式现在被标记为constexpr

#include <iostream>

#define MCR1(s) \
  ([]() { return sizeof(s); })()

#define MCR2(s) \
  ([]() { return s; })()

int main() {
  auto constexpr *s= "hello world";

  auto constexpr x1= MCR1( s );
  auto constexpr y1= MCR1( "hello world" );
  auto constexpr x2= MCR2( s );             //2
  auto constexpr y2= MCR2( "hello world" );

  std::cout << x1 << "  " << y1 << '\n';
  std::cout << x2 << "  " << y2 << '\n';
}

【问题讨论】:

  • 你原来的例子godbolt.org/g/eNVaWh的情况是不是更惊人?你在 lambda 中返回 s,它仍然可以编译。
  • @Rumburak 你又是对的。添加了另一个示例。

标签: c++ c++11


【解决方案1】:

不同之处在于(缺乏)对上下文的评估。 sizeof 未评估。

根据 N3337 (≈C++11)

§5.1 2 [expr.prim.lambda] / 11

如果一个 lambda-expression 有一个关联的 capture-default 和它的 compound-statement odr-uses this 或具有自动存储持续时间的变量,并且未明确捕获使用 odr 的实体, 然后说使用 odr 的实体被隐式捕获

§5.1.2 [expr.prim.lambda] / 12

如果 lambda 表达式 odr-使用 this 或具有自动 从其到达范围的存储持续时间,该实体应 由lambda-expression 捕获。如果 lambda 表达式 捕获 一个实体,并且该实体未在 立即包含 lambda 表达式或函数,程序是 格式不正确。

ODR 使用意味着在可能评估的上下文中使用:

§3.2 [basic.def.odr] / 2

一个表达式可能被评估,除非它是一个未评估的 操作数或其子表达式。一个变量,其名称显示为 潜在评估的表达式是 odr-used 除非它是一个对象 满足出现在常量表达式中的要求 并立即应用左值到右值的转换

由于sizeof 不是,并且s到达范围 的 lambda 表达式中,所以没关系。但是,返回 s 意味着评估它,这就是它格式错误的原因。

【讨论】:

  • 第二个引用无关紧要 - 那是为了尝试捕获无法捕获的东西。您想要紧接在此之前的句子。
  • 谢谢。仍然是这在哪里有用的问题。这显然会带来惊喜。
  • @ClaasBontus 到目前为止,我们有一个“令人惊讶”的轶事。如果 lambda 语法不允许,我会更惊讶。首先,它会被严重破坏,以至于对许多常见用途毫无用处。
【解决方案2】:

Lambda 可以在其周围范围内“看到”许多事物,而无需捕获它们:

-- 全局变量:

int x = 42;
int main() { []{ std::cout << x; }(); }

-- 静态局部变量:

int main() {
    static int x = 42;
    constexpr int y = 1337;
    []{ std::cout << x << y; }();
}

-- 功能:

int x() { return 42; }
int main() { []{ std::cout << x(); }(); }

-- 类型:

using x = std::integral_constant<int, 42>;
int main() { []{ std::cout << x::value; }(); }

-- 在未计算的上下文中使用的局部变量:

int main() {
    int x = 42;
    []{ std::cout << sizeof(x); }();
}

这自然不符合语言规则。您可以在 C++98 中使用手写的可调用对象执行相同的操作:

int main() {
    int x = 42;
    struct functor {
        int operator()() const { return sizeof(x); }
    };
    std::cout << functor{}();
}

这并不奇怪,因为sizeof 不会评估其表达式:

int main() {
    int x; // uninitialized
    using y = std::integral_constant<size_t, sizeof(x)>; // x used in a constant expression

    using z = std::integral_constant<size_t, 4>;
    static_assert(std::is_same<y, z>::value, "");

    std::cout << y::value;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-08
    • 1970-01-01
    • 2012-09-28
    相关资源
    最近更新 更多