3

考虑下面的代码,其中重载的位置f会导致一些非直观的行为。该代码在 Clang 3.4.1 和 gcc 4.8 中编译时都没有警告。

template<typename T>
struct A
{
    static const int value = sizeof(f(T()));
};

struct B
{
};

struct D : B
{
};

char f(B);

// instantiates A<D>, unqualified name lookup finds f(B) via ADL
static_assert(A<D>::value == sizeof(f(B())), ""); // passes

long f(D); // but wait, f(D) would be a better match!

// A<D> is already instantiated, f(D) is not found
static_assert(A<D>::value == sizeof(f(B())), ""); // passes

C++11 标准建议上述代码调用未定义的行为:

[temp.dep.candidate]

对于依赖于模板参数的函数调用,使用通常的查找规则找到候选函数,除了:

  • 对于使用非限定名查找或限定名查找的查找部分,仅找到来自模板定义上下文的函数声明。
  • 对于使用关联名称空间的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明。

如果函数名称是非限定 ID,并且调用格式错误或找到更好的匹配,则在关联命名空间中的查找考虑所有翻译单元中在这些命名空间中引入的具有外部链接的所有函数声明,而不仅仅是考虑在模板定义和模板实例化上下文中找到的那些声明,则程序具有未定义的行为。

上面的代码是否调用了这个特定的未定义行为?是否可以期望高质量的实施报告警告?

4

1 回答 1

1

上面的代码是否调用了这个特定的未定义行为?

是的。[温度点]/7:

依赖于模板参数的表达式的实例化上下文是在同一翻译单元中模板特化的实例化点之前声明的具有外部链接的声明集

实例化点就在第一个static_assert声明之后:

对于 […] 类模板的 […] 静态数据成员的特化,如果特化是隐式实例化的,因为它是从另一个模板特化 […] 中引用的。 否则,这种特化的实例化点紧跟在引用特化的命名空间范围声明或定义之后。

因此,如果我们考虑第二个函数声明,确实会有更好的匹配——我们不能,因为它是在A<D>::value. 根据您引用的规则,代码会导致未定义的行为。
该规则基本上是将 ODR 扩展到模板中的依赖名称查找。

是否可以期望高质量的实施报告警告?

我不会。还要考虑未定义的行为可以追溯到编译时间;如果代码导致 UB,则允许但不要求编译器发出警告或错误消息。不要期望编译器总是指出非法代码。

于 2014-11-06T23:13:31.200 回答