编译器是否不允许假设const-ref参数将保持const?

时间:2018-03-05 18:50:36

标签: c++ reference const

例如:

https://godbolt.org/g/5eUkrx

void f(const int&);

void g1() {
    const int i = 42;

    if (i == 42) f(i);
    if (i == 42) f(i);
}

void g2() {
    int i = 42;

    if (i == 42) f(i);
    if (i == 42) f(i);
}

似乎“f”变异其参数应该是UB,因此应该允许编译器假设它不会发生并相应地进行优化。然而这两个功能将产生不同的组装。

我没有标准的副本。这不保证吗?

5 个答案:

答案 0 :(得分:13)

根据标准,在C ++中将指针指向const转换为指向非const的指针然后修改它(尽管它很混乱),只要指针指向的值很长并没有被声明为const。实际上,C ++提供了一个用于执行此类转换的关键字const_cast

例如,这很好。

int a = 2;
const int* b = &a;
*const_cast<int*>(b) = 4;

但这并不是指针指向的内存位置const

const int a = 2;
const int* b = &a;
*const_cast<int*>(b) = 4;

你的例子中的编译器必须假设一个被调用的函数可能会这样做,因为它对它一无所知,并为这种情况做好准备。

答案 1 :(得分:6)

  

好像&#34; f&#34;改变其论点应该是UB

当你将它传递给函数时,它不是const。只有UB才能修改const开头的内容。因此,f可以修改i,您必须做出相应的计划。

答案 2 :(得分:4)

const引用可以将同一内存替换为在另一个线程中修改的非const变量。这里有一个有趣的话题(由LLVM / Clang开发人员提供):https://www.youtube.com/watch?v=FnGCDLhaxKU

答案 3 :(得分:2)

没有

void foo( int const& x ) {
  const_cast<int&>(x) = 7;
}

这是合法的C ++。

通过引用实际 const int来调用它会导致未定义的行为。

int x =42;
foo(x);
std::cout << x << "\n";

必须打印"7\n"

int const x = 42;
f(x);

此程序显示未定义的行为。

void g1() {
  const int i = 42;

  if (i == 42) f(i);
  if (i == 42) f(i);
}

编译器可能假定 i==42true,无论f做什么。 C ++中没有办法通过i以外的42来读取值。

void g2() {
  int i = 42;
  if (i == 42) f(i);
  if (i == 42) f(i);
}

编译器必须检查i==42是否仍然在调用f(const int&)后如果它无法检查f(const int&)中的代码(因为它已定义且有效,即使令人惊讶,f()改变i的行为,编译器也必须尊重这种可能性。但是,它可以优化第一个 i==42true,因为没有明确的方法可以更改i

void g3() {
  int j =34;
  int i = 42;
  f(j);
  if (i == 42) f(i);
  if (i == 42) f(i);
}

在这里,即使你可以想象fj的地址,找到它旁边的i,然后修改i,这是未定义的行为。标准的范围内没有办法在初始化和第一次i检查之间修改i==42

C ++的许多可达性规则是关于允许编译器假设变量仅在某些代码中被修改,而在其他代码中无法修改。

深入兔子洞:

struct foo {
  int arr[3]={1,2,3};
  int x = 42;
};

这里:

foo bob;

f(bob.arr[0]);

f 的此次调用可以修改bob.arr[0]bob.arr[1]bob.arr[2]的所有内容,但无法修改bob.x。根据标准,没有办法从数组元素到包含数组的结构的不同成员。

请注意,通过const&修改内容的代码非常危险且计划不周。但是C ++允许它。

答案 4 :(得分:1)

如果f()没有副作用,您可以使用__attribute__((const))声明它:

int f(const int&) __attribute__((const));

请注意,const函数返回void没有意义,因此我将其更改为int;并避免编译器优化掉我分配给extern的整个函数调用:

int f(const int&) __attribute__((const));

extern int j;

void g1() {
    const int i = 42;

    if (i == 42) j = f(i);
    if (i == 42) j = f(i);
}

void g2() {
    int i = 42;

    if (i == 42) j = f(i);
    if (i == 42) j = f(i);
}

现在g1()g2()编译为f()的一次调用。