Search code examples
c++visual-studio-2010c++11visual-c++static-assert

Can't use SFINAE, type traits and static_assert in MSVC10


I've been investigating the use of some judicious static assertions to improve error messages. Here's an example:

#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 will happily reject this code with the nice, clean error message I was hoping to get (once you replace nullptr with 0, anyway). But MSVC will not fire the static assertion and compiles this code just fine- even if I add some member functions and start calling on them.


Solution

  • The problem is with VC++ 2010 handling of the metafunction is_less_than_comparable, not with static_assert.

    If you change the code to:

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

    Then it will be false regardless of which overload is picked. The assertion then fires.

    Evidently the wrong overload is being selected, SFINAE isn't removing the candidate with char return type.


    Simpler test (incorrectly prints 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;
    }
    

    The compiler is, for no apparent reason, providing a bool operator<(x, x) operator, thus generating int is_less_than_comparable<T>::test<T>(bool). If a user-defined comparison operator is provided, its return type is correctly picked up. This operator isn't coming in with one of the headers, I can repro the decltype resolving to bool with no headers included.

    This generates the proper error:

    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
    

    I think this is related to VC++ not performing two-phase lookup of template dependent arguments.