Search code examples
c++c++20sfinaec++-concepts

failed to recognize concept type as bool in SFINAE


consider this example:

#include <iostream>
#include <utility>

template<typename T>
concept Printable = requires(const T a) {
    a.print();
};
template<typename T>
constexpr auto is_printable() {
    return Printable<T>;
}

template<class T, std::enable_if_t<is_printable<T>()>* = nullptr>
constexpr void do_print(T data) {
    data.print();
}

struct foo {
    void print() const {
        std::cout << "Hello World\n";
    }
};

int main() {
    foo f;
    do_print(f);
}

trying to compile this on MSVC (Version 16.9.4, /std:c++latest) will produce these errors:

Error   C2783   'void do_print(T)': could not deduce template argument for '__formal'
Error   C2672   'do_print': no matching overloaded function found

It failed to satisfy the std::enable_if_t.


I discovered the error comes from the auto in constexpr auto is_printable() { ... }, and replacing the the auto with bool will correctly compile.

template<typename T>
constexpr bool is_printable() {
    return Printable<T>;
}

I find this very bizarre, the concept Printable<T> is evaluated at compile time and should produce a constexpr bool. Why does auto fail suddenly?


Solution

  • This is an MSVC bug. Your code is correct. I highly recommend reporting the issue. Your code works correctly on GCC and Clang.


    In the meantime, I would simply drop the SFINAE. It is not really needed when you got concepts that replaces enable ifs:

    #include <iostream>
    #include <utility>
    
    template<typename T>
    concept Printable = requires(const T a) {
        a.print();
    };
    
    constexpr void do_print(Printable auto data) {
        data.print();
    }
    
    struct foo {
        void print() const {
            std::cout << "Hello World\n";
        }
    };
    
    int main() {
        foo f;
        do_print(f);
    }
    

    You can also use requires or even replace typename with your concept:

    template<typename T> requires Printable<T>
    constexpr void do_print(T data) {
        data.print();
    }
    
    template<Printable T>
    constexpr void do_print(T data) {
        data.print();
    }