来自不同翻译单元的保管功能会产生干扰吗? [英] Can consteval functions from different translation units interfere?

查看:107
本文介绍了来自不同翻译单元的保管功能会产生干扰吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试探究inline函数的含义,并偶然发现了这个问题.考虑这个小程序(演示):

I am trying to dig into implications of a function being inline and stumbled upon this issue. Consider this small program (demo):

/* ---------- main.cpp ---------- */
void other();

constexpr int get()
{
    return 3;
}

int main() 
{
    std::cout << get() << std::endl;
    other();
}

/* ---------- other.cpp ---------- */
constexpr int get()
{
    return 4;
}

void other()
{
    std::cout << get() << std::endl;
}

在不进行优化的情况下进行编译时,该程序将产生以下输出:

When compiled without optimizations, the program yields the following output:

3
3

可能不是我们想要的,但是至少我可以解释一下.

Which might be not what we want, but at least I can explain it.

  1. 不需要编译器在编译时计算constexpr函数的结果,因此决定将其推迟到运行时.
  2. 函数上的
  3. constexpr表示inline
  4. 我们的get()函数碰巧具有不同的实现方式
  5. 我们没有将get()函数声明为静态
  6. 链接器只能选择get()函数的一种实现方式
  1. The compiler is not required to compute results of constexpr functions on compile time, so it decided to postpone it to runtime.
  2. constexpr on functions implies inline
  3. Our get() functions happened to have different implementations
  4. We did not declare get() functions static
  5. The linker must chose only one implementation of the get() function

碰巧的是,链接器从main.cpp中选择了get(),并返回了3.

And it so happened that the linker chose get() from main.cpp, which returned 3.

现在我不了解的部分.我只是将get()函数从constexpr更改为consteval .现在,要求编译器在编译期间(即在链接时间之前)对值进行计算(对吗?).我希望get()函数根本不会出现在目标文件中.

Now to the part I don't get. I simply changed get() functions from constexpr to consteval. Now the compiler is required to compute the value during compile time, i.e. before the link time (right?). I'd expect get() functions not to be present in object files at all.

但是当我运行它(演示)时,我的输出完全相同!怎么可能呢?..我的意思是,我知道这是未定义的行为,但这不是重点.应该在编译时计算的值如何干扰其他翻译单元?

But when I run it (demo), I have exactly the same output! How can this be?.. I mean yes, I understand that this is undefined behavior, but this is not the point. How come that the values that should have been computed on compile time interfered with other translation unit?

UPD::我知道此功能在clang中未实现,但该问题仍然适用.是否可以让合格的编译器表现出这种行为?

UPD: I am aware that this feature is listed as unimplemented in clang, but the question is applicable anyway. Is a conformant compiler allowed to exhibit such a behavior?

推荐答案

具有相同内联函数的两个定义的程序是格式错误的程序,不需要进行诊断.

A program with two definitions of the same inline function is an ill-formed program, no diagnostics required.

该标准对格式错误的程序的运行时或编译时行为没有任何要求.

The standard places no requirements on the runtime or compile-time behavior of an ill-formed program.

现在,正如您所想象的那样,C ++中没有编译时间".几乎每个C ++实现都可以编译文件,链接文件,构建二进制文件然后运行它,而C ++标准则围绕这一事实进行了介绍.

Now, there is no "compile time" in C++ as you are imagining it. While almost every C++ implementation compiles files, links them, builds a binary, then runs it, the C++ standard tip-toes around this fact.

它讨论翻译单元,将它们组合到程序中时会发生什么,以及该程序的运行时行为是什么.

It talks about translation units, and what happens when you put them together into a program, and what that program's runtime behaviour is.

...

在实践中,您的编译器可能正在构建从符号到某些内部结构的映射.它正在编译第一个文件,然后在第二个文件中仍在访问该映射.内联函数的新定义?只是跳过它.

In practice, your compiler could be building a map from symbol to some internal structure. It is compiling your first file, and then in the second file it is still accessing that map. A new definition of the same inline function? Just skip it.

第二,您的代码必须产生一个编译时常数表达式.但是,在您使用编译上下文常量的情况下,编译时间常量表达式不是可观察的属性,并且在链接甚至运行时执行此操作都没有副作用!在没有任何阻止的情况下.

Second, your code must produce a compile time constant expression. But a compile time constant expression is not an observable property in the context where you used it, and there are no side effects to doing it at link or even run time! And under as-if there is nothing preventing that.

consteval的意思是如果我运行此命令,并且违反了允许其成为常量表达式的规则,那么我应该出错,而不是依赖于非常量表达式".与必须在编译时运行"相似,但不相同.

consteval is saying "if I run this and the rules that permit it being a constant expression are violated, I should error rather than fall back on non-constant expression". This is similar to "it must be run at compile time", but it is not the same.

要确定其中发生了什么,请尝试以下操作:

To determine which of these is happening, try this:

template<auto x>
constexpr std::integral_constant< decltype(x), x > constant = {};

现在将打印行替换为:

std::cout << constant<get()> << std::endl;

这使得推迟评估运行/链接时间变得不切实际.

this makes putting off the evaluation to run/link time as impractical.

这将区分编译器很聪明和缓存get"与编译器稍后在链接时对其进行评估",因为确定要调用哪个ostream& <<要求实例化constant<get()>的类型,转弯需要评估get().

That will distinguish between the "compiler is being clever and caching get" from "compiler is evaluating it later at link time", because determining which ostream& << to call requires instantiating the type of constant<get()>, which in turn requires evaluating get().

编译器通常不会将重载分辨率推迟到链接时间上.

Compilers tend not to defer overload resolution to link time.

这篇关于来自不同翻译单元的保管功能会产生干扰吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆