检查类型是否可以显式转换

check if type can be explicitly converted

本文关键字:显式转换 是否 类型 检查      更新时间:2023-10-16

如何确定(在<type_traits>精神中)一种类型是否显式转换为另一种类型?例如,我想检查一些class/struct FF::explicit operator double const & () const;的存在,但是,同时,F不应该显式地转换为floatlong 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性状应该以何种方式修复。基本的选择是:

  1. 通过返回当前的static_cast表达式修复is_explicitly_convertible,不再使is_explicitly_convertible依赖于is_constructible
  2. 从标准中删除is_explicitly_convertible

我们考虑了第一种选择,但结果是,对于"显式可转换"的实际含义存在着截然不同的理解。一些人认为static_cast正确地表达了这一点,另一些人认为固定的is_constructible也会为is_explicitly_convertible提供更好的含义。因此,本文建议将is_explicitly_convertible从工作草案中删除。现在这应该没什么害处,因为还没有任何东西依赖于那个特殊的定义。如果事实证明,这个特征仍然是有用的,它可以被添加到标准的另一个版本中。

该定义中没有已知的错误,但对于它是否编纂了"显式可转换"的含义,存在反对意见是正确的:-

  • D1) From显式转换为To =df To可从From构建
  • D2) From显式转换为To =df From可以静态转换为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_castTo ;这就意味着替换:

  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);

该定义的简化版本(忽略诸如Fromvoid, 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_convertiblestd::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构造函数和一个删除的赋值操作符。