【问题标题】:C++ inconsistency between gcc and clanggcc和clang之间的C ++不一致
【发布时间】:2014-04-11 01:41:23
【问题描述】:

我遇到了gcc(版本4.8.14.8.2)和clang(版本3.33.4)之间的C++不一致。我想知道哪个是正确的。这是程序:

template < typename T > struct Result {};
template < typename T > struct Empty {};

template < typename T >
struct Bad_Type_Fcn {
    typedef typename Empty< T >::type type;
};

template < typename T >
Result< T >
f( const T& ) {
    return Result< T >();
}

template< class U >
Result< typename Bad_Type_Fcn< U >::type >
f( const U&, int ) {
    return Result< typename Bad_Type_Fcn< U >::type >();
}

int main() {
    (void)f< int >(42);
}

显然,这段代码并不意味着做任何事情;它是对 Boost Range 库中出现的东西的积极简化(f 简化了 make_iterator_range)。 Bad_Type_Fcn 是一个类型函数(技术上是 struct),它不应该被实例化,因为对于任何 TEmpty&lt;T&gt;::type 从不存在。这个structf() 的第二个模板特化的存在本身并不是一个错误。 IRL,f()Bad_Type_Fcn 不为空的某些类型提供了一些功能。然而,这不是这里的问题,这就是我简化这些的原因。我仍然希望 f() 适用于 Bad_Type_Fcn 为空的类型。

我正在使用{g++|clang++} [-std=c++0x] -pedantic -Wall -Wextra -c 进行编译。语言标准的选择似乎没有什么不同。使用clang,程序编译时不会出现错误或警告。使用gcc,我得到一个错误:

weird.cpp: In instantiation of ‘struct Bad_Type_Fcn<int>’:
weird.cpp:17:5:   required by substitution of ‘template<class U> Result<typename Bad_Type_Fcn<T>::type> f(const U&, int) [with U = int]’
weird.cpp:22:26:   required from here
weird.cpp:6:43: error: no type named ‘type’ in ‘struct Empty<int>’
         typedef typename Empty< T >::type type;

似乎正在发生的事情是 clang 消除了 f() 的第二个重载,可能(?)基于调用仅使用 1 个参数,整数 42,而第二个重载需要 2论据。另一方面,gcc 并没有消除第二次重载,而是尝试实例化struct Bad_Type_Fcn&lt;int&gt;,这会导致错误。

如果我删除对f() 的调用中的显式实例化,并改为写(void)f(42);,则不一致会消失。

哪个编译器是正确的?

【问题讨论】:

  • GCC 不应该在给定的代码示例中实例化第二个重载。
  • 你可能想在 gcc 4.9 上尝试一下;这真的很有趣,特别是因为据我所知,这应该是一个 SFINAE 案例。非常有趣的测试用例,并且大大减少了它。
  • +1,但术语更正(在这样的问题中,它可能很重要):不涉及函数模板 specialisation (因为它们不存在部分专业化) .您有两个 distinct 函数模板,它们恰好具有相同的名称,因此彼此 overload
  • @MatthieuM.:这花了几天时间,你可以想象...
  • 我认为编译器根本不应该实例化#2,但似乎是 GCC 的情况,并且它无法编译,因为它不是 SFINAE 失败。 SFINAE 不适用于Bad_Type_Fcn 体内的typedef typename Empty&lt; T &gt;::type type。如果 Empty&lt;T&gt;::type 不存在,您需要使嵌套的 type typedef 消失,SFINAE 才能工作。

标签: c++ gcc clang language-lawyer template-specialization


【解决方案1】:

我记得 WG21 对此的核心讨论,其中一位 Clang 开发人员通过引用 14.7.1p7 为自己的立场辩护

如果重载解决过程可以在不实例化类模板定义的情况下确定要调用的正确函数,则未指定该实例化是否实际发生。

另一方面,对于格式不正确的程序(在进行所需的实例化时就是这种情况),没有“要调用的正确函数”这样的概念,所以我同意另一个人的立场在那次讨论中,他说他看不到这允许 Clang 走那条路。

在 p7 的示例中,它显示了格式良好的代码,无论是否进行额外的实例化。

在任何情况下,即使允许 Clang 这样做,您的程序的良好格式也会依赖于特定的偶然事件(未指定的行为)。因此,该标准不再要求您的程序被接受,老实说,我不知道这意味着什么。我认为这样的代码格式不正确。

【讨论】:

  • 这不与 [temp.over]/1 矛盾吗?替换是扣除的一部分,每个功能模板都必须扣除。
  • @dyp 您能否详细说明“这与 [temp.over]/1 不矛盾”是什么意思?
  • [temp.over]/1 似乎描述了涉及函数模板的重载解析过程。它指定模板参数推导发生在为每个函数模板。因此,替换也应该作为此过程的一部分发生,据我所知,这会导致类型无效。
  • @dyp 如果你不实例化类模板Bad_Type_Fcn,那么你就不会出现错误。正如我所说,实例化确实是必需的,因为它是替换过程的一部分,但 14.7.1[temp.inst]/7 使 clang 无法进行实例化。我认为类型然后保持不完整,然后Bad_Type_Fcn&lt; U &gt;::type 是一个直接的推断错误,因为invalid_class_type::member 总是失败。
  • @mmd 我认为如果您删除显式模板参数,则程序不再是格式错误的,因为类型推导会失败(匹配错误)。参数列表为(int),参数列表为(const U&amp;, int)。显式指定模板参数是一个问题,因为在匹配 (int)(const int&amp;, int) 之前,该参数将被替换。
猜你喜欢
  • 2021-04-02
  • 2021-08-22
  • 2016-12-10
  • 1970-01-01
  • 2019-12-20
  • 1970-01-01
  • 2016-08-01
  • 2017-10-09
  • 1970-01-01
相关资源
最近更新 更多