Как использовать SFINAE с нулевой функцией-членом?

У меня есть шаблон класса Bird с логическим параметром шаблона can_fly. В зависимости от этого значения я хочу включить функцию-член с сигнатурой void fly();.

Это мой код:

#include <type_traits>

template<bool can_fly>
class Bird {
public:
    template<typename void_t = typename std::enable_if<can_fly>::type>
    void_t fly() { /* ... */ }
};

int main() {
    Bird<true> flyingBird;
    flyingBird.fly();
    Bird<false> flightlessBird;

    return 0;
}

Этот код отлично компилируется в Visual Studio 2015, но GCC жалуется на то, что в третьей строке main нет "типа с именем 'type' в 'struct std::enable_if'".

Я думал, что тот факт, что в случае false нет ::type, был всей сутью SFINAE. Может кто-нибудь объяснить мне, что я сделал неправильно и каков правильный подход?


person Daniel Wolf    schedule 11.12.2016    source источник


Ответы (1)


Как упоминается в этом ответе:

enable_if работает, потому что подстановка аргумента шаблона привела к ошибке, поэтому эта подстановка исключается из набора разрешений перегрузки, и компилятор рассматривает только другие жизнеспособные перегрузки.

В вашем случае замены нет, потому что can_fly известно в момент создания экземпляра. Вы можете создать параметр шаблона dummy по умолчанию bool, чтобы заставить SFINAE работать правильно:

template<bool can_fly>
class Bird {
public:
    template<bool X = can_fly, typename = typename std::enable_if<X>::type>
    void fly() { /* ... */ }
};

пример wandbox

person Vittorio Romeo    schedule 11.12.2016
comment
Теперь я понимаю! В моем коде все выражение std::enable_if<can_fly>::type может быть оценено до разрешения перегрузки и приводит к ошибке. Принимая во внимание, что в вашем коде выражение std::enable_if<X>::type может быть оценено только после того, как известно X. Большое спасибо! - person Daniel Wolf; 12.12.2016
comment
проголосовали за, но в производственном коде вам также нужен X == can_fly внутри enable_if. Это запрещает вызов Bird<false>::fly<true>();.. Также вы можете использовать ... и _t, чтобы избежать typename и немного сократить нотацию, как template<bool X = can_fly, std::enable_if_t<X == can_fly && X>...> void fly() {}. - person TemplateRex; 20.12.2016