【问题标题】:Clang vs. GCC: Error in unevaluated context breaks SFINAEClang vs. GCC:未评估上下文中的错误破坏了 SFINAE
【发布时间】:2016-07-14 10:34:31
【问题描述】:

my previous question 继续工作时,我遇到了 clang 和 GCC 的不同行为。
我需要检查成员函数指针,因为我需要知道该函数是否被继承。

在 SFINAE 上下文中比较成员函数指针时,成员函数 Foo::foo() 存在,但其主体包含最终无法编译的代码 (x.hello())。

以下代码使用 clang 编译。 然而,GCC 似乎评估了 Foo::foo() 的函数体并以错误 ('struct Caller' has no member named 'hello') 退出,尽管处于未评估的 SFINAE 上下文中(或者我希望如此)。

#include <iostream>
#include <type_traits>

struct Foo
{
    template <typename T> void foo(T&& x) { x.hello(); }
};

struct Caller
{    
    template <typename T>
    auto call(T&& x) -> decltype(
        std::enable_if_t<
            std::is_same<
                decltype(&T::template foo<decltype(*this)>),
                void (T::*)(decltype(*this))
            >::value
        >())
    {
        //x.foo(*this);
    }
};

int main()
{
  Caller c;
  c.call(Foo());
}

我测试过:

  • clang 3.8.0
  • g++ 6.1.0

两者的编译器选项:-std=c++14 -O2 -Wall -pedantic -pthread

live example

我的问题:

  1. 谁是对的? Clang 还是 GCC?
  2. 如何获取代码以使用 GCC 编译?

【问题讨论】:

  • 提一下编译器的版本和选项怎么样? x.hello() 绝对不在 SFINAE 上下文中。

标签: c++ templates gcc sfinae clang++


【解决方案1】:

回答第二个问题

如何获取代码以使用 GCC 进行编译?

我会简化传递给decltype() 的表达式。如果 call 方法的编译依赖于 x.foo(*this); 的编译,那么你应该使用它。

struct Foo
{
    template <typename T> void foo(T&& x) { x.hello(); }
};

struct Caller
{    
    template <typename T>
    auto call(T&& x, int) -> decltype(x.foo(*this))
    {
        //x.foo(*this);
    }
    template <typename T>
    void call(T&&, char){ std::cout << "hello" << std::endl;}
};

int main()
{
  Caller c;
  c.call(Foo(), 0);
}

Demo here.


我认为 OP 与 gcc 的问题在于函数的地址(或者如果没有明确地采用,则衰减到函数指针)。我认为这是标准中的一个极端案例。如果需要方法Foo::foo,则x.hello() 需要存在(编译);一般来说,取某物的地址可以满足这一点,但在未评估的上下文 (decltype()) 中,我不确定这是否适用 - 当然 clang 不需要它存在(MSVC 也不需要)。

在这方面,

谁是对的? Clang 还是 GCC?

我怀疑 clang 实现了对标准的更宽松的阅读,并且可能更正确的阅读。 decltype() 的操作数是未计算的操作数,请参见 [dcl.type.simple]/4

decltype 说明符的操作数是未计算的操作数(子句 [expr])。

【讨论】:

  • 我需要检查成员函数指针,因为我想知道该函数是否被继承(见我的linked previous question)。
  • @JonathanMee。正确,这就是替换失败的原因,然后将方法从重载决议中删除。 W.r.t OP 代码,我认为问题在于函数的地址。我认为这是标准中的一个极端案例。如果需要该方法,x.hello 将需要存在(编译);一般来说,取某物的地址可以满足这一点,但在未经评估的情况下 (decltype()),我不确定这是否适用。
  • @m.s.此时,这是一个硬错误——gcc 认为该地址是必需的(因为&amp;);另一方面,clang 似乎丢弃了指针所需的存在,因此错误不是一个硬错误。
  • @m.s.我想是的,是的,应该报告。
  • @m.s.更改foo 的声明会解决您的问题吗?例如:template &lt;typename T&gt; decltype(declval&lt;T&gt;().hello(), void()) foo(T&amp;&amp; x) { x.hello(); }
猜你喜欢
  • 2018-09-23
  • 2020-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-17
  • 2014-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多