Не удается использовать SFINAE, типовые признаки и static_assert в MSVC10

#c #visual-studio-2010 #c 11 #visual-c #static-assert

#c #visual-studio-2010 #c 11 #visual-c #статическое утверждение

Вопрос:

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

 #include <type_traits>
template<typename T> struct is_less_than_comparable {
    template<typename Test> static char test(decltype(*static_cast<Test*>(nullptr) < *static_cast<Test*>(nullptr)));
    template<typename Test> static int test(...);
    static const bool value = std::is_same<char, decltype(test<T>(true))>::value;
};
template<typename K, typename V> class map {
public:
    static_assert(is_less_than_comparable<K>::value, "Key type must be less-than comparable!");
};
struct x {};
int main() {
    map<x, int> intmap;
}
  

IDEONE с радостью отклонит этот код с приятным, понятным сообщением об ошибке, которое я надеялся получить (в любом случае, как только вы замените nullptr на 0). Но MSVC не запускает статическое утверждение и отлично компилирует этот код — даже если я добавлю некоторые функции-члены и начну вызывать их.

Комментарии:

1. Код компилируется не для меня. Нужны ли какие-то дополнительные заголовочные файлы?

2. Я думаю, это связано с тем, что VC не выполняет двухфазный поиск аргументов, зависящих от шаблона.

3. @BenVoigt : У меня сложилось впечатление, что VC 2010 просто напросто ломается при попытке использовать decltype для SFINAE. Я вообще отказался от попыток его использовать: -[

4. Вы должны проверить недавно добавленные свойства оператора для повышения. Признаки типа

Ответ №1:

Проблема заключается в обработке метафункции в VC 2010 is_less_than_comparable , а не в static_assert .

Если вы измените код на:

 static const bool value = std::is_same<double, decltype(test<T>(true))>::value;
  

Тогда это будет false независимо от того, какая перегрузка выбрана. Затем срабатывает утверждение.

Очевидно, выбрана неправильная перегрузка, SFINAE не удаляет кандидата с char возвращаемым типом.


Более простой тест (неправильно печатается 1 ):

 #include <type_traits>
template<typename T> struct is_less_than_comparable {
    template<typename Test> static char test(decltype(*static_cast<Test*>(nullptr) < *static_cast<Test*>(nullptr)) b);
    template<typename Test> static int test(...);
    static const bool value = std::is_same<char, decltype(test<T>(true))>::value;
};
struct x {};
#include <iostream>
int main() {
    std::cout << is_less_than_comparable<x>::value << std::endl;
}
  

Компилятор без видимой причины, предоставляя bool operator<(x, x) оператор, таким образом генерация int is_less_than_comparable<T>::test<T>(bool) . Если предоставляется определяемый пользователем оператор сравнения, его возвращаемый тип выбран правильно. Этот оператор не входит ни в один из заголовков, я могу воспроизвести decltype разрешение на bool без включенных заголовков.

Это генерирует соответствующую ошибку:

 decltype(*(x*)nullptr < *(x*)nullptr) b;

error C2676: binary '<' : 'x' does not define this operator or a conversion to a type acceptable to the predefined operator
  

Я думаю, это связано с тем, что VC не выполняет двухфазный поиск аргументов, зависящих от шаблона.

Ответ №2:

Я не уверен, что вы ищете в ответе здесь, поэтому вот type trait, который отлично работает в VC 2010:

 #include <type_traits>

namespace supports
{
    namespace details
    {
        struct return_t { };

        template<typename T>
        static Tamp; make();
    }

    template<typename T>
    details::return_t operator <(T constamp;, T constamp;);

    template<typename T>
    struct less_than : std::integral_constant<
        bool,
        !std::is_same<
            decltype(details::make<T>() < details::make<T>()),
            details::return_t
        >::value
    > { };
}