SFINAE 用户定义的转换运算符

SFINAE User-Defined-Conversion Operator

我正在尝试使用内省设计进行模板化的用户定义转换。

在 C++20 中,我可以执行以下操作:

    template<typename T>
    operator T() const
    {
        if constexpr( requires { PFMeta<T>::from_cursor(*this); } )
        {
            return PFMeta<T>::from_cursor(*this);
        }
        else
        {
            T x;
            std::memcpy(&x, &data, sizeof(x));
            return x;
        }
    }

即如果定义了 PFMeta<T>::from_cursor(const struct PFCursor&) 则使用它,否则执行 memcpy。 (godbolt 中更长的示例:https://godbolt.org/z/abed1vsK3

我喜欢这种方法,但不幸的是,这个库也需要在 C++17 上工作。

所以我一直在尝试将 SFINAE 作为概念的替代品,但这非常棘手。 我终于设法得到了类似的东西,但使用的是模板化方法 as 而不是用户定义的转换运算符本身:

template<typename T, typename = void>
struct has_from_cursor : std::false_type { };

template<typename T>
struct has_from_cursor<T, decltype( PFMeta<T>::from_cursor(std::declval<struct PFCursor>()), void() ) > : std::true_type { };

// ...

    template<class T>
    std::enable_if_t<has_from_cursor<T>::value, T> as() const
    {
        return PFMeta<T>::from_cursor(*this);
    }

    template<class T>
    std::enable_if_t<!has_from_cursor<T>::value, T> as() const
    {
        T x;
        std::memcpy(&x, &data, sizeof(x));
        return x;
    }

我尝试了以下编译但不起作用的方法(我无法使用它进行转换):

    template<typename T>
    operator std::enable_if_t<has_from_cursor<T>::value, T>() const
    {
        return PFMeta<T>::from_cursor(*this);
    }

    template<typename T>
    operator std::enable_if_t<!has_from_cursor<T>::value, T>() const
    {
        T x;
        std::memcpy(&x, &data, sizeof(x));
        return x;
    }

此处更长的示例:https://godbolt.org/z/5r9Mbo18h

所以两个问题:

谢谢!

Can I do effectively what I can do with concepts with just SFINAE in C++17 for the user defined conversion operator?

考虑到 C++17 支持 if constepr。鉴于您已经开发了 has_from_cursor 继承自 std::true_typestd::false_type 的自定义类型特征,您可以将其用于 if constexp.

我的意思是(注意:代码未经测试)

template<typename T>
operator T() const
{ // .............VVVVVVVVVVVVVVVVVVVVVVVVV
    if constexpr( has_from_cursos<T>::value )
    {
        return PFMeta<T>::from_cursor(*this);
    }
    else
    {
        T x;
        std::memcpy(&x, &data, sizeof(x));
        return x;
    }
}

Is there a simpler way to do the as approach than the type traits & enable_if? Ideally I'd like to do something like define the default templated method and then have a specialisation to be preferred if the condition is there (i.e. there is a meta class with a static member function defined).

我想你可以试试 tag-dispatching...

我的意思是从 operator T() 调用 'as()',并附加一个 int 参数

template<typename T>
operator T() const
{ return as<T>(0); } // <-- call as() with a int

其中有一个 as() 特定于 from_cursos class 已启用,接收未使用的 int 并且只是 SFINAE enabled/disabled 到 decltype()

template <typename T>  // ........accept an int; best match
decltype(PFMeta<T>::from_cursor(*this)) as (int) const
 { return PFMeta<T>::from_cursor(*this); }

和一个通用的 as(),收到一个 long

template <typename T>
T as (long) const // <-- accept a long; worst match
 {
   T x;
   std::memcpy(&x, &data, sizeof(x));
   return x;
 }

诀窍是未使用的参数:int.

当启用专用 as() 时,如果首选因为接受 int 所以是更好的匹配。

当禁用专用 as() 时,保留通用 as() 作为 better-than-nothig 匹配。