Простая SFINAE Задача условного объявления функции-члена - PullRequest
2 голосов
/ 21 апреля 2020

Я прочитал следующие темы:

нет типа с именем 'type' в 'struct std :: enable_if

Выбор функции-члена с использованием различных условий enable_if

«Что случилось с моим SFINAE» redux: члены класса условных шаблонов?

Однако я, похоже, не могу заставить эту довольно простую проблему SFINAE работать на g cc и msv c:

#include <type_traits>
#include <iostream>

template<typename A, typename B>
class Test {
public:

  template<typename X=A, typename = typename std::enable_if<std::is_same<X, void>::value, void>::type >
  void foo() {
    std::cout << "A";
  }

  template<typename X=A, typename = typename std::enable_if<!std::is_same<X, void>::value, void>::type >
  void foo() {
    std::cout << "B";
  }


};

int main(int argc, char **argv) {

  Test<int, float> t;

  t.foo();

  return 0;
}

Фактический результат:

A = недействительно: полная ошибка:

main.cpp:15:8: error: 'template<class A, class B> template<class X, class> void Test<A, B>::foo()' cannot be overloaded with 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
   15 |   void foo() {
      |        ^~~
main.cpp:10:8: note: previous declaration 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
   10 |   void foo() {
      |        ^~~

A = int: полная ошибка:

main.cpp:15:8: error: 'template<class A, class B> template<class X, class> void Test<A, B>::foo()' cannot be overloaded with 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
   15 |   void foo() {
      |        ^~~

main.cpp:10:8: note: previous declaration 'template<class A, class B> template<class X, class> void Test<A, B>::foo()'
   10 |   void foo() {
      |        ^~~

main.cpp: In function 'int main(int, char**)':

main.cpp:26:9: error: no matching function for call to 'Test<int, float>::foo()'
   26 |   t.foo();
      |         ^

main.cpp:10:8: note: candidate: 'template<class X, class> void Test<A, B>::foo() [with X = X; <template-parameter-2-2> = <template-parameter-1-2>; A = int; B = float]'
   10 |   void foo() {
      |        ^~~

main.cpp:10:8: note:   template argument deduction/substitution failed:

main.cpp:9:26: error: no type named 'type' in 'struct std::enable_if<false, void>'
    9 |   template<typename X=A, typename = typename std::enable_if<std::is_same<X, void>::value, void>::type >
      |                          ^~~~~~~~

Ожидаемый результат

A = void: выходы "A"

A = int: выходы "B"

Я хочу реализовать другую (дополнительную) функцию-член на основе параметра шаблона. Однако кажется, что я не могу сделать enable_if зависимым от типов шаблонов классов, но я не уверен почему. Согласно связанным темам, код выше выглядит правильным. Не могли бы вы объяснить, почему это не работает?

Live Link

Ответы [ 2 ]

1 голос
/ 21 апреля 2020

Примечания из cppreference показывают подобное и объясняют, почему это не работает:

Распространенной ошибкой является объявление двух шаблонов функций, которые отличаются только аргументами шаблона по умолчанию. Это не работает, потому что объявления обрабатываются как переопределения одного и того же шаблона функции (аргументы шаблона по умолчанию не учитываются в эквивалентности шаблона функции).

/* WRONG */

struct T {
    enum { int_t,float_t } m_type;
    template <typename Integer,
              typename = std::enable_if_t<std::is_integral<Integer>::value>
    >
    T(Integer) : m_type(int_t) {}

    template <typename Floating,
              typename = std::enable_if_t<std::is_floating_point<Floating>::value>
    >
    T(Floating) : m_type(float_t) {} // error: treated as redefinition
};

/* RIGHT */

struct T {
    enum { int_t,float_t } m_type;
    template <typename Integer,
              std::enable_if_t<std::is_integral<Integer>::value, int> = 0
    >
    T(Integer) : m_type(int_t) {}

    template <typename Floating,
              std::enable_if_t<std::is_floating_point<Floating>::value, int> = 0
    >
    T(Floating) : m_type(float_t) {} // OK
};

Применяя такое же исправление к вашему коду, выдает желаемый B:

#include <type_traits>
#include <iostream>

template<typename A, typename B>
class Test {
public:

  template<typename X = A,std::enable_if_t<std::is_same<X, void>::value, int> = 0>
  void foo() {
    std::cout << "A";
  }

  template<typename X=A,std::enable_if_t<!std::is_same<X, void>::value, int> = 0>
  void foo() {
    std::cout << "B";
  }


};

В вашем коде два шаблона функций отличаются только по умолчанию аргументы. После исправления второй параметр либо int = 0, либо ошибка подстановки.

1 голос
/ 21 апреля 2020

Вот версия C ++ 17:

template<typename X=A>
std::enable_if_t<std::is_same_v<X, void>> foo() {
    std::cout << "A";
}

template<typename X=A>
std::enable_if_t<!std::is_same_v<X, void>> foo() {
    std::cout << "B";
}

(тип по умолчанию для enable_if - void, который используется в качестве типа для функции)

Вы можете также сделайте это с constexpr if:

void foo() {
    if constexpr (std::is_same_v<A, void>) {
        std::cout << "A";      
    } else {
        std::cout << "B";
    }
}

C ++ 11:

template<typename X=A>
typename std::enable_if<std::is_same<X, void>::value>::type foo() {
    std::cout << "A";
}

template<typename X=A>
typename std::enable_if<!std::is_same<X, void>::value>::type foo() {
    std::cout << "B";
}
...