Как использовать SFINAE для трех методов с компилятором Intel C++ (ICC)?

Я пытаюсь добавить поддержку icc в один из своих проектов, но у меня возникают проблемы с SFINAE, когда методов больше двух. Вот простой пример проблемы:

#include <iostream>

template<std::size_t Selector>
struct impl {
    template<bool Enable = true, typename std::enable_if<Selector == 1 && Enable, int>::type = 0>
    static void apply(){
        std::cout << "First selector" << std::endl;
    }

    template<bool Enable = true, typename std::enable_if<Selector == 2 && Enable, int>::type = 0>
    static void apply(){
        std::cout << "Second selector" << std::endl;
    }

    template<bool Enable = true, typename std::enable_if<Selector == 3 && Enable, int>::type = 0>
    static void apply(){
        std::cout << "Big selector" << std::endl;
    }
};

int main(){
    impl<1>::apply();
    impl<2>::apply();
    impl<3>::apply();

    return 0;
}

Это прекрасно работает с g++ и clang++, но не компилируется с icc:

test.cpp(16): error: invalid redeclaration of member function template "void impl<Selector>::apply() [with Selector=1UL]" (declared at line 11)
      static void apply(){
                  ^
          detected during instantiation of class "impl<Selector> [with Selector=1UL]" at line 22

test.cpp(11): error: invalid redeclaration of member function template "void impl<Selector>::apply() [with Selector=3UL]" (declared at line 6)
      static void apply(){
                  ^
          detected during instantiation of class "impl<Selector> [with Selector=3UL]" at line 24

compilation aborted for test.cpp (code 2)

Есть ли обходной путь для этого с icc ? Я бы не хотел менять слишком много кода, у меня есть эта проблема в нескольких местах моего проекта.

Я использую icc 16.0.2.164.

Спасибо


person Baptiste Wicht    schedule 31.03.2015    source источник
comment
Очевидным обходным путем будет частичная специализация. Даже если это означает перемещение apply в impl_base<Selector> и добавление using impl_base<Selector> в impl. Это полностью устраняет необходимость в SFINAE.   -  person MSalters    schedule 31.03.2015
comment
Какую версию ICC вы используете?   -  person usr1234567    schedule 31.03.2015
comment
Это неправильно, в любом случае диагностика не требуется. Для любого данного экземпляра impl не может быть создана допустимая специализация по крайней мере для двух из трех apply.   -  person T.C.    schedule 31.03.2015
comment
Что вы пытаетесь достичь?   -  person rubenvb    schedule 31.03.2015
comment
Я использую icc 16.0.2.164. Я просто хочу выбрать функцию на основе одного из параметров типа родительского класса. @Т.С. Могли бы вы развить? В моем случае в классе есть другие параметры шаблона, и у меня есть эта проблема в нескольких местах моего проекта, я бы не хотел менять слишком много кода.   -  person Baptiste Wicht    schedule 31.03.2015


Ответы (2)


Для кода, показанного в вопросе, явная специализация функции-члена, как показано в ответе @Jarod42, вероятно, является самым простым.

Когда SFINAE создает функцию-член шаблона класса на основе параметров шаблона класса, получить правильный код может быть непросто. [temp.res]/p8:

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

Хитрость заключается в том, чтобы выражение SFINAE зависело от параметров шаблона функции-члена:

template<std::size_t Selector>
struct impl {
    template<std::size_t S = Selector, typename std::enable_if<S == 1, int>::type = 0>
    static void apply(){
        std::cout << "First selector" << std::endl;
    }

    template<std::size_t S = Selector, typename std::enable_if<S == 2, int>::type = 0>
    static void apply(){
        std::cout << "Second selector" << std::endl;
    }

    template<std::size_t S = Selector, typename std::enable_if<S == 3, int>::type = 0>
    static void apply(){
        std::cout << "Big selector" << std::endl;
    }
};

Обратите внимание, что у каждого из вышеперечисленных apply() есть одна допустимая специализация.

person T.C.    schedule 31.03.2015
comment
Это действительно работает, спасибо, но почему это не работает с простым добавлением параметра шаблона bool? И почему это работает с gcc/clang? - person Baptiste Wicht; 31.03.2015
comment
@BaptisteWicht Потому что в вашем коде нет возможности создать допустимую специализацию, скажем, для первого apply в impl<2>. Независимо от значения параметра bool подпись остается недействительной. Поскольку диагностика не требуется, компиляторы могут, но не обязаны отклонять ее. - person T.C.; 31.03.2015

Специализация – это решение:

template<std::size_t Selector>
struct impl {
    static void apply();
};

template<>
void impl<1>::apply(){
    std::cout << "First selector" << std::endl;
}

template<>
void impl<2>::apply(){
    std::cout << "Second selector" << std::endl;
}

template<>
void impl<3>::apply(){
    std::cout << "Big selector" << std::endl;
}
person Jarod42    schedule 31.03.2015