2016-07-14 2 views
3

Продолжая работу с my previous question, я столкнулся с различным поведением clang и GCC.
Мне нужно проверить указатель функции-члена, потому что мне нужно знать, наследуется ли функция или нет.Clang vs. GCC: ошибка в необоснованных перерывах контекста SFINAE

При сравнении указателей функций-членов в контексте 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()); 
} 

я тестировал с:

  • лязгом 3.8.0
  • г ++ 6.1.0

опции компилятора для обоих: -std=c++14 -O2 -Wall -pedantic -pthread

live example

Мои вопросы:

  1. , кто прав? Clang или GCC?
  2. Как я могу получить код для компиляции с GCC?
+3

Как насчет упоминания версий и вариантов компиляторов? 'x.hello()' определенно не в контексте SFINAE. –

ответ

2

В ответ на второй вопрос

Как я могу получить код для компиляции с 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 лежит в адресе принимается функции (или распадающийся указатель функции, если явно не принимается). Я думаю, что это что-то вроде угла в стандарте. x.hello() должен существовать (компилироваться), если требуется метод Foo::foo; вообще говоря, адрес чего-то удовлетворяет этому, но в неоценимом контексте (decltype()), я не уверен, что это применимо - конечно, clang не требует его существования (и MSVC).

В этой связи

Кто прав? Clang или GCC?

Я подозреваю, что clang реализует более разрешительное чтение стандарта и, вероятно, более правильное чтение. Операнд decltype() является неоцененным операндом, см. [dcl.type.simple]/4;

Операнд спецификатора decltype является неоцененным операндом (раздел [expr]).

+0

Мне нужно проверить указатель на функцию-член, потому что я хочу знать, наследуется функция или нет (см. Мой [связанный предыдущий вопрос] (http://stackoverflow.com/a/38347040/678093)). –

+0

@Niall Я боюсь понять, почему это работает. 'Caller' не имеет метода' hello'. Как можно создать экземпляр 'void Foo :: foo (Caller &&)'? –

+1

@JonathanMee. Правильно, поэтому замена не выполняется, а затем метод удаляется из разрешения перегрузки. W.r.t в OP-коде, я думаю, проблема заключается в адресе, который берется функция. Я думаю, что это что-то вроде угла в стандарте. «X.hello» должен существовать (компилироваться), если этот метод требуется; вообще говоря, адрес чего-то удовлетворяет этому, но в неоценимом контексте ('decltype()'), я не уверен, что это применимо. – Niall

Смежные вопросы