Search code examples
c++templatestype-traitsenable-if

enable_if with is_move_constructible allows non-movable types, but requires does not


I have a non-movable structure and a templated class in which I want to have a function that exists only when the type is movable (using enable_if and type_traits). However, it seems that despite std::is_move_constructible_v returns false, the function still exists and can be executed. However, when I changed the code to use requires clause, everything works as intended. Why is that?

#include<bits/stdc++.h>

class NonMovable{
    public:
        NonMovable(const NonMovable&) = default;
        NonMovable(NonMovable&&) = delete;
        
        NonMovable& operator =(const NonMovable&) = default;
        NonMovable& operator = (NonMovable&&) = delete;
        
        NonMovable() = default;
};

template<typename T>
struct Foo{
    template<typename = std::enable_if<std::is_move_constructible_v<T>,bool>>
    void foo(T&& t){ // allowed
        // ...
    }
};

template<typename T>
struct Foo{
    void foo(T&& t) requires std::is_move_constructible_v<T>{ // not allowed
        // ...
    }
};

int main(){
    NonMovable nonMovable;
    Foo<NonMovable> x;
    std::cout << std::is_move_constructible_v<NonMovable> << "\n"; // 0
    x.foo(std::move(nonMovable));
}

Solution

  • If you only want to disable that foo function for non move constructible types, you could make the template parameter a dependent type:

    template<typename T>
    struct Foo{
        template<class U = T, class = std::enable_if_t<
                                          std::is_same_v<T,U>&&
                                          std::is_move_constructible_v<U>>>
        void foo(T&&){ // not allowed
            // ...
        }
    };
    

    Demo