ошибка компиляции для SFINAE на VS2017

#include <type_traits>
#define str_cat(first, second) first##second
#define has_xxx(member_name) \
template<class T, class U = void> \
struct str_cat(has_, member_name): std::false_type {};\
template<class T>\
struct str_cat(has_, member_name)<T, typename SFINAE<typename T::member_name>::type>\
    : std::true_type {};\
template<class T>\
struct str_cat(has_, member_name)<T, typename SFINAE<decltype(T::member_name)>::type>\
    : std::true_type {};

template<class T>
struct SFINAE {
    using type = void;
};

struct A {
    int i = 0;
    void foo() {}
    using void_t = void;
};

struct B {
    int j = 0;
    void goo() {}
    using void_t = void;
};

has_xxx(i)
has_xxx(foo)
has_xxx(j)
has_xxx(goo)
has_xxx(void_t) //compile error if `has_xxx(i)` appears at the head

int main()
{

    //has_i<A>::value; // true
    //has_i<B>::value; // false

    has_foo<A>::value; // true
    has_foo<B>::value; // false
    has_goo<B>::value; // true

    has_void_t<A>::value; // true
    has_void_t<B>::value; // true

    return 0;
}

В VS2017 не удается скомпилировать
https://gcc.godbolt.org/z/JkOhLi
ошибка: C2752 'шаблон': несколько частичных специализаций соответствуют списку аргументов шаблона.
Но это нормально для gcc и clang.http://coliru.stacked-crooked.com/a/6b9490f6b127ae88
Если изменить порядок макроса, он компилируется:

has_xxx(foo)
has_xxx(i) //now compiles successfully
has_xxx(j)
has_xxx(goo)
has_xxx(void_t) //compile error if `has_xxx(i)` appears at the head

Или просто измените имя члена в структуре A:

struct A {
    int k = 0; // i -> k, now compiles successfully !!!!
    void foo() {}
    using void_t = void;
};

Не могу понять причину.
Имеет ли значение порядок макросов или это баг MSVC на SFINAE?


person rsy56640    schedule 28.08.2018    source источник


Ответы (1)


Это похоже на ошибку MSVC, однако ее можно легко избежать следующим образом:

#include <type_traits>

#define str_cat(first, second) first##second
#define custom_trait(trait_name, expr) \
  template<class T, class U = void> \
  struct trait_name: std::false_type {}; \
  template<class T> \
  struct trait_name<T, std::void_t<expr>> : std::true_type {};
#define has_xxx(member_name) \
  custom_trait(str_cat(has_type_, member_name), typename T::member_name) \
  custom_trait(str_cat(has_value_, member_name), decltype(T::member_name)) \
  template<class T>\
  using str_cat(has_, member_name) = \
      std::bool_constant<str_cat(has_type_, member_name)<T>::value \
                      || str_cat(has_value_, member_name)<T>::value>;

Примечание: этот код (как и ваш) не позволяет обнаруживать методы.

Я предлагаю вам сообщить о проблеме (Help -> Send Feedback -> Report a problem — это Visual Studio).

person Nikita Kniazev    schedule 29.08.2018