简体   繁体   中英

How can I prevent implicit conversions in std::is_constructible

Let's say, I have a few different classes:

class constructible_from_float {
public: 
    constructible_from_float(float);
};
class constructible_from_double {
public: 
    constructible_from_double(double);
};
class constructible_from_long_double {
public: 
    constructible_from_long_double(long double);
};

And then I want to do something based on what type are they constructible from (simplified example):

#include <type_traits>
template <typename T>
constexpr size_t foo() {
    if constexpr (std::is_constructible<T, float>::value) {
        return 1;
    } else if constexpr (std::is_constructible<T, double>::value) {
        return 2;
    } else if constexpr (std::is_constructible<T, long double>::value) {
        return 3;
    } else
        return -1;
}

But the problem is, all of these return 1 :

[[maybe_unused]] auto float_result = foo<constructible_from_float>();
[[maybe_unused]] auto double_result = foo<constructible_from_double>();
[[maybe_unused]] auto long_double_result = foo<constructible_from_long_double>();

在此处输入图像描述

I'm aware that the reason for the behavior is implicit conversions between types. Is there a legit (usable on at the very least three major compilers: msvc , gcc and clang ) way to force the compiler to distinguish between these types.

I'm not allowed to to change the classes ( constructible_from_float , etc.) but can do everything else. Anything provided by the stable versions of the compilers is ok (including c++2a ).

You have to fool the C++ compiler in revealing to you what implicit conversion it wants to use, then use SFINAE to pull the rug from under its feet, and fail to instantiate the template, but SFINAE, so it's not an error.

#include <type_traits>
#include <iostream>

class constructible_from_float {
public:
    constructible_from_float(float);
};
class constructible_from_double {
public:
    constructible_from_double(double);
};
class constructible_from_long_double {
public:
    constructible_from_long_double(long double);
};


template<typename T> class convertible_only_to {

public:
    template<typename S, typename=std::enable_if_t<std::is_same_v<T,S>>>
    operator S() const
    {
        return S{};
    }
};


template <typename T>
constexpr int foo() {
    if constexpr (std::is_constructible<T,
              convertible_only_to<float>>::value) {
            return 1;
    } else
    if constexpr (std::is_constructible<T,
              convertible_only_to<double>>::value) {
            return 2;
        } else
    if constexpr (std::is_constructible<T,
              convertible_only_to<long double>>::value) {
            return 3;
    } else
        return -1;
}

struct not_constructible_from_anything {};

int main()
{
    std::cout << foo<constructible_from_float>() << std::endl;
    std::cout << foo<constructible_from_double>() << std::endl;
    std::cout << foo<constructible_from_long_double>() << std::endl;
    std::cout << foo<not_constructible_from_anything>() << std::endl;

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM