使用 SFINAE 测试指针类型是否可以 static_cast 到另一个指针类型

Jon*_*per 2 c++ sfinae static-cast c++14

背景

我正在写一个可移动的QScopedPointer;基本上std::unique_pointer有一些额外的访问器。我在访问 C++11 兼容编译器之前就开始了它,但现在我决心把它做好(即使我在重新发明轮子)。

问题

让我们调用我的智能指针MyUniquePointer

我需要知道 type 是否U *可以T *通过 a转换为 type static_cast,特别是:

template<class T, class Cleanup>
class MyUniquePointer
{
...
template<class U, class CleanupU, class = std::enable_if_t<detail::is_safely_castable<U, T>()>
MyUniquePointer(MyUniquePointer<U, CleanupU> && other) noexcept
    : d(static_cast<T *>(other.release()))
{}
...
Run Code Online (Sandbox Code Playgroud)

前半题

我的第一次尝试是static_cast在 an 内部使用enable_if,但您不能使用 的地址std::declval()来获取static_cast!

如果指向的指针U可以static_cast指向T使用模板魔术的指针,有没有办法使用测试?

尝试的解决方法

基于cppreference这个答案,我尝试创建一个模板测试来模拟 static_cast 何时合法,如果向下转换,则安全。到目前为止,这是我汇总的内容:

#include <iostream>
#include <type_traits>
template <class From, class To>
struct is_safely_castable //should probably be is_safely_castable_pointer or something
        : std::integral_constant<bool,
               std::is_pointer<From>() && std::is_pointer<To>()
            && ((std::is_base_of<To, From>()/* && std::has_virtual_destructor<From>()*/)
               || std::is_convertible<From, To>()
               || std::is_same<To,void *>()
               || std::is_same<From, void *>())>
{
};

struct base_type
{
    base_type() = default;
    base_type(base_type &&) = default;
    base_type(const base_type &) = default;
    virtual ~base_type() { }
    base_type &operator=(const base_type &) = default;
    base_type &operator=(base_type &&) = default;
};

struct derived_type : public base_type
{
};

struct unrelated_type
{
};

struct convertible_type
{
    convertible_type(const base_type *) {}
    convertible_type(base_type *) {}
    convertible_type() = default;

    operator base_type *() { return nullptr; }
};

int main(int argc, char *argv[])
{
    (void)(argc);
    (void)(argv);

    base_type *b = new base_type;
    derived_type *d = new derived_type;
    unrelated_type *u = new unrelated_type;
    uint32_t *i32 = new uint32_t{1};
    uint64_t *i64 = new uint64_t{2};
    void *v = static_cast<derived_type *>(d);

    std::cout << std::boolalpha
        << "Base to base:        " << (bool)static_cast<base_type *>(b) << '\n'
        << "Base to derived:     " << (bool)static_cast<derived_type *>(b) << '\n'
        << "Derived to base:     " << (bool)static_cast<base_type *>(d) << '\n'
        << "Unrelated to base:   false\n" //<< static_cast<base_type *>(u) << '\n'
        << "uint32 to uint64:    false\n" //<< static_cast<uint64_t *>(i32) << '\n'
        << "uint64 to uint32:    false\n" //<< static_cast<uint32_t *>(i64) << '\n'
        << "Base to void:        " << (bool)static_cast<void *>(b) << '\n'
        << "Void to derived:     " << (bool)static_cast<derived_type *>(v) << '\n'
        << "Convertible to base: false\n" //<< static_cast<base_type *>(c) << '\n'
        << "Base to convertible: false\n";//<< static_cast<convertible_type *>(b) << '\n';


    std::cout << "-----------\n"
        << "Base to base:        " << is_safely_castable<base_type *, base_type *>() << '\n'
        << "Base to derived:     " << is_safely_castable<base_type *, derived_type *>() << '\n'
        << "Derived to base:     " << is_safely_castable<derived_type *, base_type *>() << '\n'
        << "Unrelated to base:   " << is_safely_castable<unrelated_type *, base_type *>() << '\n'
        << "uint32 to uint64:    " << is_safely_castable<uint32_t *, uint64_t *>() << '\n'
        << "uint64 to uint32:    " << is_safely_castable<uint64_t *, uint32_t *>() << '\n'
        << "Base to void:        " << is_safely_castable<base_type *, void *>() << '\n'
        << "Void to derived:     " << is_safely_castable<void *, derived_type *>() << '\n'
        << "Convertible to base: " << is_safely_castable<convertible_type *, base_type *>() << '\n'
        << "Base to convertible: " << is_safely_castable<base_type *, convertible_type *>() << '\n';

    delete b;
    delete d;
    delete u;
    delete i32;
    delete i64;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

魔杖盒链接

返回:

Base to base:        true
Base to derived:     true
Derived to base:     true
Unrelated to base:   false
uint32 to uint64:    false
uint64 to uint32:    false
Base to void:        true
Void to derived:     true
Convertible to base: false
Base to convertible: false
-----------
Base to base:        true
Base to derived:     false
Derived to base:     true
Unrelated to base:   false
uint32 to uint64:    false
uint64 to uint32:    false
Base to void:        true
Void to derived:     true
Convertible to base: false
Base to convertible: false
Run Code Online (Sandbox Code Playgroud)

我的问题的后半部分是这种解决方法是否在正确的轨道上,更具体地说,是否|| std::is_convertible<From, To>()应该包括在内。is_convertible将指向类型的指针作为模板参数传递时,是否可以返回 true?上面的代码包括我自己的尝试让它工作。

脚注:我知道base_type *强制转换derived_type *成功,但我不是编译器,不能做出这个假设。

Jar*_*d42 6

看来你想要:

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

template <typename T, typename U>
struct is_safely_castable<T, U,
                          std::void_t<decltype(static_cast<U>(std::declval<T>()))>>
: std::true_type
{};
Run Code Online (Sandbox Code Playgroud)

演示