简体   繁体   English

C ++ 11:格式错误的调用是未定义的行为?

[英]C++11: ill-formed calls are undefined behavior?

§ 14.6.4.2 from N3485 states the following about dependent candidate function lookup: N3485的第14.6.4.2节规定了有关依赖候选函数查找的以下内容:

If the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior. 如果在关联的名称空间中进行查找时考虑了格式错误或将找到更好的匹配项,则考虑了所有函数声明以及在所有转换单元中的这些名称空间中引入的外部链接的所有函数声明,而不仅仅是考虑了在模板定义和模板中找到的那些声明实例化上下文,则程序具有未定义的行为。

What exactly does it mean for a call to be "ill-formed", and how would an ill-formed call be selected by the lookup? 呼叫“格式错误”到底意味着什么?查找将如何选择格式错误的呼叫? Also, why does it matter that a better match would be found if all translation units were considered? 另外,如果考虑所有翻译单元,找到更好的匹配为什么很重要呢?

What exactly does it mean for a call to be "ill-formed" 呼叫“格式错误”的确切含义是什么

Formally, ill-formed is defined by [defns.ill.formed] as not well-formed, and a well-formed program is defined by [defns.well.formed] as: 形式上,[defns.ill.formed]将格式错误的代码定义为格式不正确,[defns.well.formed]将格式良好的程序定义为:

C++ program constructed according to the syntax rules, diagnosable semantic rules, and the One Definition Rule (3.2). 根据语法规则,可诊断的语义规则和“一个定义规则”(3.2)构造的C ++程序。

So an ill-formed call is one with invalid syntax or a diagnosable error such as passing the wrong number of arguments, or arguments which cannot be converted to the parameter types, or an overload ambiguity. 因此,格式错误的调用是具有无效语法或可诊断错误的调用,例如传递了错误数量的参数,或者无法转换为参数类型的参数,或重载歧义。

how would an ill-formed call be selected by the lookup? 查找将如何选择格式错误的呼叫?

I think it's saying "if (the call would be ill-formed || would find a better match) had the lookup within the associated namespaces considered all the function declarations with external linkage ...", which means you have undefined behaviour if considering other functions would have found equal or better matches. 我认为这是在说“如果(调用会格式不正确||会找到更好的匹配项)在关联命名空间中进行的查找考虑了具有外部链接的所有函数声明...”,这意味着如果考虑使用外部链接,您将具有未定义的行为其他功能将找到相等或更好的匹配。 Equally good matches would make the call ambiguous, ie ill-formed, and better matches would have resulted in a different function being called. 同样,好的匹配将使调用变得模棱两可,即格式不正确,而更好的匹配将导致调用另一个函数。

So if in another context the call would have been ambiguous or caused another sort of error, but succeeds due to only considering a limited set of names in the instantiation and definition contexts, it's undefined. 因此, 如果在另一个上下文中调用将是模棱两可的或引起另一种错误,但由于仅在实例化和定义上下文中考虑了一组有限的名称而成功,则该调用是未定义的。 And if in another context the call would have chosen a better match, that's also undefined. 如果呼叫在其他情况下会选择更好的匹配项,那么这也是不确定的。

Also, why does it matter that a better match would be found if all translation units were considered? 另外,如果考虑所有翻译单元,找到更好的匹配为什么很重要呢?

I think the reason for the rule is to disallow situations where instantiating the same template specialization in two different contexts results in it calling two different functions, eg if in one translation unit the call finds one function, and in another translation unit it finds a different function, you'll get two different instantiations of the same template, which violates the ODR, and only one instantiation will be kept by the linker, so the instantiation that's not kept by the linker will get replaced by one which calls a function that wasn't even visible where the template was instantiated. 我认为,该规则的原因是不允许出现以下情况:在两个不同的上下文中实例化同一模板特化会导致它调用两个不同的函数,例如,如果在一个翻译单元中调用找到一个函数,而在另一个翻译单元中找到另一个函数,函数,您将获得同一模板的两个不同的实例化,这违反了ODR,并且链接程序仅保留一个实例化,因此链接程序不保留的实例化将被调用该函数的一个实例代替。甚至在实例化模板的位置都看不到。

That's similar (if not already covered by) the last sentence of the previous paragraph: 这与上一段的最后一句话类似(如果尚未涵盖):

A specialization for any template may have points of instantiation in multiple translation units. 任何模板的专业化可能在多个翻译单元中具有实例化点。 If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required. 如果根据一个定义规则(3.2),两个不同的实例化点赋予模板专业化不同的含义,则程序格式错误,无需诊断。

Page 426 of the C++ ARM (Ellis & Stroustrup) gives a bit of context for that text (and I believe for 14.6.4.2 as well) and explains it more concisely and clearly than I did above: C ++ ARM(Ellis和Stroustrup)的第426页提供了该文本的一些上下文信息(我相信也适用于14.6.4.2),并且比我上面的内容更简洁明了:

This would seem to imply that a global name used from within a template could be bound to different objects or functions in different compilation units or even at different points within a compilation unit. 这似乎意味着从模板内使用的全局名称可以绑定到不同编译单元中甚至编译单元内不同点的不同对象或函数。 However, should that happen, the resulting template function or class is rendered illegal by the "one-definition" rule (§7.1.2). 但是,如果发生这种情况,则通过“一个定义”规则(第7.1.2节)将生成的模板函数或类视为非法。

There's another related formulation of the same rules in [basic.def.odr]/6 [basic.def.odr] / 6中还有另一条相同规则的表述

The problem is that namespaces can be defined piecemeal, so there is no one place that is guaranteed to define all of the members of a namespace. 问题在于名称空间可以零散地定义,因此没有一个地方可以保证定义名称空间的所有成员。 As a result, different translation units can see different sets of namespace members. 结果,不同的翻译单元可以看到不同的名称空间成员集。 What this section says is that if the part that isn't seen would affect lookup, the behavior is undefined. 本节的意思是,如果看不到的部分会影响查找,则行为是不确定的。 For example: 例如:

namespace mine {
    void f(double);
}

mine::f(2); // seems okay...

namespace mine {
    void f(char);
}

mine::f(2); // ambiguous, therefore ill-formed

The rule says that the first call to f(2) produces undefined behavior because it would have been ill-formed if all of the overloads in mine had been visible at that point. 该规则说,对f(2)的第一次调用会产生未定义的行为,因为如果那时在mine所有重载都可见的话,它将是错误的。

Building on @tletnes' partial answer , I think I've come up with a simple program that triggers this particular undefined behavior. @tletnes的部分答案为基础 ,我想我想出了一个触发该特定未定义行为的简单程序。 Of course it uses multiple translation units. 当然,它使用多个翻译单元。

cat >alpha.cc <<EOF
#include <stdio.h>
void customization_point(int,int) { puts("(int,int)"); }
#include "beta.h"
extern void gamma();
int main() {
    beta(42);
    gamma();
}
EOF

cat >gamma.cc <<EOF
#include <stdio.h>
void customization_point(int,double) { puts("(int,double)"); }
#include "beta.h"
void gamma() { beta(42); }
EOF

cat >beta.h <<EOF
template<typename T>
void beta(T t) {
    customization_point(t, 3.14);
}
EOF

Compiling this program with different optimization levels changes its behavior. 用不同的优化级别编译该程序会更改其行为。 This is all right, according to the Standard, because the call in "alpha.cc" invokes undefined behavior. 根据标准,这没关系,因为“ alpha.cc”中的调用会调用未定义的行为。

$ clang++ alpha.cc gamma.cc -O1 -w ; ./a.out
(int,int)
(int,int)
$ clang++ alpha.cc gamma.cc -O2 -w ; ./a.out
(int,int)
(int,double)

When I read this rule I imagine the code similar to the following is at least part of what was being considered: 当我阅读此规则时,我认为类似于以下内容的代码至少是所考虑内容的一部分:

int foo(int a; int b){ printf("A"); }

int main(){
   foo(1, 1.0);
}

int foo(int a, double b){ printf("B"); }

or 要么

int foo(int a);

int main(){
   foo(1);
}

int foo(int a, double b){ printf("B"); }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM