1

Consider the following code:

// Preamble
#include <iostream>
#include <type_traits>

// Wrapper
template <class From>
struct wrapper
{
    // Implicit conversion
    template <class To, class = typename std::enable_if<
        std::is_convertible<From, To>::value
    >::type>
    constexpr operator To() const noexcept;

    // Explicit conversion
    template <class To, class = typename std::enable_if<
        !std::is_convertible<From, To>::value
        && std::is_constructible<To, From>::value
    >::type>
    explicit constexpr operator To() const noexcept;
};

// Main
int main(int argc, char* argv[])
{
    wrapper<int> x;
    double y = x;
    return 0;
}

Ideally this code would make the conversion operator implicit when From is implicitly convertible to To, and make the conversion operator explicit when To is explicitly constructible from From.

However, the code currently does not compile because from the compiler standpoint, both conversion operators have the same signature.

Question: Would there be any way to trick the compiler to produce the expected behavior?


Answer: full code based on Quentin's answer:

// Preamble
#include <iostream>
#include <type_traits>

// Wrapper
template <class From>
struct wrapper
{
    // Implicit conversion
    template <class To, typename std::enable_if<
        std::is_convertible<From, To>::value,
    int>::type = 0>
    constexpr operator To() const noexcept(noexcept(From{})) {
        return From{};
    }

    // Explicit conversion
    template <class To, typename std::enable_if<
        !std::is_convertible<From, To>::value
        && std::is_constructible<To, From>::value,
    int>::type = 0>
    explicit constexpr operator To() const noexcept(noexcept(From{})) {
        return From{};
    }
};

// Main
int main(int argc, char* argv[])
{
    wrapper<int> x;
    double y = x;
    return 0;
}
Vincent
  • 57,703
  • 61
  • 205
  • 388

1 Answers1

3

Yes, just replace your class = typename std::enable_if<...>::type pattern with typename std::enable_if<..., int>::type = 0. The SFINAE parameter is then a non-type template parameter of a different type, and the functions overload corerctly.

Quentin
  • 62,093
  • 7
  • 131
  • 191