检查类型是否可以显式转换
check if type can be explicitly converted
如何确定(在<type_traits>
精神中)一种类型是否显式转换为另一种类型?例如,我想检查一些class
/struct
F
的F::explicit operator double const & () const;
的存在,但是,同时,F
不应该显式地转换为float
或long double
(类似pred< double const & >::value && !pred< float >::value && !pred< long double >::value
)。
注意,std::is_convertible< From, To >::value
检查"是否From可以使用隐式转换转换为 to "。但是我希望确定是否存在显式转换操作符。
并且,如果可能的话,"如何确定类型From转换为,即对类型 to 的引用"?
您需要定义您自己的:
template <class U, class T>
struct is_explicitly_convertible
{
enum {value = std::is_constructible<T, U>::value && !std::is_convertible<U, T>::value};
};
您的测试集位于场外链接,并且与您的原始测试集相比有所不同所以我将逐字复制我在这里谈论的测试集:
static_assert(is_explicitly_convertible< double, double >::value, "1");
static_assert(is_explicitly_convertible< double &, double >::value, "2");
static_assert(is_explicitly_convertible< double const, double >::value, "3");
static_assert(is_explicitly_convertible< double const &, double >::value, "4");
static_assert(is_explicitly_convertible< double, double const & >::value, "5");
static_assert(is_explicitly_convertible< double &, double const & >::value, "6");
static_assert(is_explicitly_convertible< double const, double const & >::value, "7");
static_assert(is_explicitly_convertible< double const &, double const & >::value, "8");
static_assert(!is_explicitly_convertible< double, double & >::value, "9"); // not a ref
static_assert(is_explicitly_convertible< double &, double & >::value, "10");
static_assert(!is_explicitly_convertible< double const, double & >::value, "11");
static_assert(!is_explicitly_convertible< double const &, double & >::value, "12");
static_assert(is_explicitly_convertible< double, double const >::value, "13");
static_assert(is_explicitly_convertible< double &, double const >::value, "14");
static_assert(is_explicitly_convertible< double const, double const >::value, "15");
static_assert(is_explicitly_convertible< double const &, double const >::value, "16");
static_assert(is_explicitly_convertible< AA const &, A const & >::value, "=&1.a");
static_assert(is_explicitly_convertible< CC const &, C const & >::value, "=&1.b");
static_assert(is_explicitly_convertible< BB const &, B const & >::value, "=&1.c");
static_assert(!is_explicitly_convertible< AA const &, A & >::value, "&1.a");
static_assert(!is_explicitly_convertible< CC const &, C & >::value, "&1.b");
static_assert(!is_explicitly_convertible< BB const &, B & >::value, "&1.c");
static_assert(is_explicitly_convertible< AA const, A const & >::value, "=1.a");
static_assert(is_explicitly_convertible< CC const, C const & >::value, "=1.b");
static_assert(is_explicitly_convertible< BB const, B const & >::value, "=1.c");
//static_assert(!is_explicitly_convertible< AA const, A >::value, "=2.a"); // ???????????????
//static_assert(!is_explicitly_convertible< CC const, C >::value, "=2.b");
//static_assert(!is_explicitly_convertible< BB const, B >::value, "=2.c");
static_assert(!is_explicitly_convertible< AA const, A & >::value, "=3.a"); // good!
static_assert(!is_explicitly_convertible< CC const, C & >::value, "=3.b"); //
static_assert(!is_explicitly_convertible< BB const, B & >::value, "=3.c"); //
static_assert(!is_explicitly_convertible< AA const, A && >::value, "=4.a"); // not interesting
static_assert(!is_explicitly_convertible< CC const, C && >::value, "=4.b"); //
static_assert(!is_explicitly_convertible< BB const, B && >::value, "=4.c"); //
static_assert(!is_explicitly_convertible< AA const, B const & >::value, "=5.a");
static_assert(!is_explicitly_convertible< AA const, C const & >::value, "=5.b");
static_assert(!is_explicitly_convertible< BB const, A const & >::value, "=5.c");
static_assert(!is_explicitly_convertible< BB const, C const & >::value, "=6.a");
static_assert(!is_explicitly_convertible< CC const, A const & >::value, "=6.b");
static_assert(!is_explicitly_convertible< CC const, B const & >::value, "=6.c");
static_assert(!is_explicitly_convertible< AA const, B & >::value, "=7.a");
static_assert(!is_explicitly_convertible< AA const, C & >::value, "=7.b");
static_assert(!is_explicitly_convertible< BB const, A & >::value, "=7.c");
static_assert(!is_explicitly_convertible< BB const, C & >::value, "=8.a");
static_assert(!is_explicitly_convertible< CC const, A & >::value, "=8.b");
static_assert(!is_explicitly_convertible< CC const, B & >::value, "=8.c");
static_assert(!is_explicitly_convertible< AA const, B >::value, "=9.a"); // very subtle moment (see class AA above)
static_assert(!is_explicitly_convertible< AA const, C >::value, "=9.b");
static_assert(is_explicitly_convertible< BB const, A >::value == std::is_constructible< A, A && >::value, "=9.c"); // (see class BB above)
static_assert(!is_explicitly_convertible< BB const, C >::value, "=10.a");
static_assert(!is_explicitly_convertible< CC const, A >::value, "=10.b");
static_assert(!is_explicitly_convertible< CC const, B >::value, "=10.c");
static_assert(is_explicitly_convertible< AA, A & >::value, "~1.a");
static_assert(is_explicitly_convertible< CC, C & >::value, "~1.b");
static_assert(is_explicitly_convertible< BB, B & >::value, "~1.c");
//static_assert(!is_explicitly_convertible< AA, A >::value, "~2.a"); // ???????????????
//static_assert(!is_explicitly_convertible< CC, C >::value, "~2.b");
//static_assert(!is_explicitly_convertible< BB, B >::value, "~2.c");
static_assert(is_explicitly_convertible< AA, A const & >::value, "~3.a"); // convertible
static_assert(is_explicitly_convertible< CC, C const & >::value, "~3.b"); //
static_assert(is_explicitly_convertible< BB, B const & >::value, "~3.c"); //
static_assert(!is_explicitly_convertible< AA, B const & >::value, "~4.a");
static_assert(!is_explicitly_convertible< AA, C const & >::value, "~4.b");
static_assert(!is_explicitly_convertible< BB, A const & >::value, "~4.c");
static_assert(!is_explicitly_convertible< BB, C const & >::value, "~5.a");
static_assert(!is_explicitly_convertible< CC, A const & >::value, "~5.b");
static_assert(!is_explicitly_convertible< CC, B const & >::value, "~5.c");
static_assert(std::is_convertible< double, double const & >::value, "5*");
static_assert(!std::is_convertible< double, double & >::value, "9*");
如果你使用gcc 4.7.2(也许更早,我还没有检查),那么c++ 11标准库解决了这个问题:
std::is_explicitly_convertible<From,To>
在<type_traits>
但是,您将利用该版本中的错误c++ 11标准库。这个trait模板不应该在那里,因为已根据N3047(2010)从标准中删除。
如果你使用的是gcc 4.8.1(或者4.8;我还没查过)那么这个特征就是如果你想要它,你必须重新滚动你自己的。
但是只有在gcc 4.7.2的<type_traits>
中检查定义才是人类的首先,这样做表明GNU实现者考虑了这个特性是std::is_constructible<To,From>
:
/// is_explicitly_convertible
template<typename _From, typename _To>
struct is_explicitly_convertible
: public is_constructible<_To, _From>
{ };
有人可能会想:但是当然。
那为什么不能继续呢?N3047解释说:
剩下的问题是,同样受到影响的
is_explicitly_convertible
性状应该以何种方式修复。基本的选择是:
- 通过返回当前的static_cast表达式修复
is_explicitly_convertible
,不再使is_explicitly_convertible
依赖于is_constructible
。- 从标准中删除
is_explicitly_convertible
。我们考虑了第一种选择,但结果是,对于"显式可转换"的实际含义存在着截然不同的理解。一些人认为
static_cast
正确地表达了这一点,另一些人认为固定的is_constructible
也会为is_explicitly_convertible
提供更好的含义。因此,本文建议将is_explicitly_convertible
从工作草案中删除。现在这应该没什么害处,因为还没有任何东西依赖于那个特殊的定义。如果事实证明,这个特征仍然是有用的,它可以被添加到标准的另一个版本中。
该定义中没有已知的错误,但对于它是否编纂了"显式可转换"的含义,存在反对意见是正确的:-
- D1)
From
显式转换为To
=dfTo
可从From
构建 - D2)
From
显式转换为To
=dfFrom
可以静态转换为To
我不会为这个争论争论(我自己甚至还没有弄清楚区别是什么),但我会建议你只是付你的钱,做你的选择。
如果你支持D1),那么你可以简单地从gcc 4.7.2 <type_traits>
如果你支持D2,那么你可以剽窃和改编std::is_convertible<From,To>
的定义从gcc 4.8.1 <type_traits>
。这个定义调用了内部辅助功能,你必须追踪。适应你您想要的是更改SFINAE测试 From
是否可以隐式转换为To
对一个测试 From
可以是static_cast
到To
;这就意味着替换:
template<typename _From1, typename _To1>
static decltype(__test_aux<_To1>(std::declval<_From1>()), __one())
__test(int);
:
template<typename _From1, typename _To1>
static decltype(__test_aux<_To1>(static_cast<_To1>(std::declval<_From1>())), __one())
__test(int);
该定义的简化版本(忽略诸如From
是void
, To
是一个函数或数组类型)在static_assert
s中测试的类型:
template<typename From, typename To>
struct is_explicitly_convertible
{
template<typename T>
static void f(T);
template<typename F, typename T>
static constexpr auto test(int) ->
decltype(f(static_cast<T>(std::declval<F>())),true) {
return true;
}
template<typename F, typename T>
static constexpr auto test(...) -> bool {
return false;
}
static bool const value = test<From,To>(0);
};
D1定义或缩减后的D2定义支持所有63个static_assert
s。(我用g++ 4.7.2和4.8.1,-g;-O0;-Wall;-std=c++11
编译,也用您使用的-std=gnu++1y
)
你最近的解决方案表明你已经自己去了D2学校,使用不同的实现风格。
在这两个中,直到我发现它有什么问题,我更喜欢D1定义,根据gcc 4.7.2,仅仅是因为它是最简单的,特别是,它是一个平凡的导数std::is_constructible
.
我现在回答这个问题是为了添加一个c++ 20风格的概念更新,并给出一个关于std::is_convertible
和std::convertible_to
的标准库的小注释。据我所知,std::is_convertible
检查隐式可转换性, std::convertible_to
测试隐式可转换性和显式可转换性。我们可以窃取std::convertible_to
概念实现的第二部分,得出如下内容:
template <class T, class U>
concept explicitly_convertible_to = requires(T t) { static_cast<U>(t); };
如果你没有c++20的概念,这样也可以:
#include <type_traits>
template <class T, class U, class = void>
struct is_explicitly_convertible_to_impl : std::false_type {};
template <class T, class U>
struct is_explicitly_convertible_to_impl<
T, U, std::void_t<decltype(static_cast<U>(std::declval<T>()))>>
: std::true_type {};
template <class T, class U>
struct is_explicitly_convertible_to
: is_explicitly_convertible_to_impl<T, U> {};
template <class T, class U>
inline constexpr bool is_explicitly_convertible_to_v =
is_explicitly_convertible_to<T, U>::value;
对于那些想知道为什么这对于更详细的元编程是必要的人:确实存在一种类型显式而不是隐式可转换为另一种类型的情况。主要是:给出一个explicit
构造函数和一个删除的赋值操作符。
- 整数类型应该显式转换(例如"int"到"无符号")还是只会增加混乱?
- 隐式转换是否应该在模板参数的上下文中工作?
- 从双精度转换为整数的显式类型是否始终检查整数溢出?
- std::vector 范围构造函数可以调用显式转换吗?
- 从 uint8_t 到 int 的隐式转换出错了,当显式转换进展顺利时
- 从 int 显式转换为用户定义的类 c++
- 自动(toCast)显式转换是否计划在未来C++标准?
- 为什么C++隐式转换有效,而显式转换无效?
- 使用分配进行显式转换
- 使用 static_cast、dynamic_cast 或显式转换进行派生指向 Base 指针转换的指针不会调用 base 函数
- 返回语句中的C++11显式转换运算符/构造函数
- 如何解决Visual Studio 2012不支持显式转换运算符的问题
- 通过显式转换函数初始化枚举类类型的静态constexpr类成员
- C++ 为什么我们需要从一种类型显式转换为另一种类型
- 当显式转换是必需的并且隐式转换将不起作用时
- C++显式转换和隐式转换
- 如何争论多个显式转换函数
- C++显式转换构造函数
- 用户定义类(c++)是否有默认的显式转换函数
- 检查类型是否可以显式转换