如何防止 std::is_constructible 中的隐式转换
How can I prevent implicit conversions in std::is_constructible
比方说,我有几个不同的 类:
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);
};
然后我想根据它们可构造的类型(简化示例)做一些事情:
#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;
}
但问题是,所有这些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>();
我知道该行为的原因是类型之间的隐式转换。
是否有一种合法的(至少可用于三个主要编译器:msvc
、gcc
和 clang
)的方法来强制编译器区分这些类型。
我不能更改 类(constructible_from_float
等),但可以做其他任何事情。
编译器的稳定版本提供的任何东西都可以(包括 c++2a
)。
你得骗过C++编译器告诉你它要用什么隐式转换,然后用SFINAE来揭发它,实例化模板失败,但是SFINAE,所以它不是一个错误。
#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;
}
比方说,我有几个不同的 类:
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);
};
然后我想根据它们可构造的类型(简化示例)做一些事情:
#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;
}
但问题是,所有这些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>();
我知道该行为的原因是类型之间的隐式转换。
是否有一种合法的(至少可用于三个主要编译器:msvc
、gcc
和 clang
)的方法来强制编译器区分这些类型。
我不能更改 类(constructible_from_float
等),但可以做其他任何事情。
编译器的稳定版本提供的任何东西都可以(包括 c++2a
)。
你得骗过C++编译器告诉你它要用什么隐式转换,然后用SFINAE来揭发它,实例化模板失败,但是SFINAE,所以它不是一个错误。
#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;
}