获取任何C ++可取消引用类型的基础类型

时间:2019-05-14 17:32:39

标签: c++ pointers templates

我有一个函数可以创建基础类型为P的新对象。这里的P是可取消引用的类型,如指针或智能指针。

template<typename P>
auto make_new()

例如,对于指针和智能指针,

struct A
{
    int a = 3;
};

A* a = make_new<A*>();
std::cout << a->a << std::endl;
delete a;

std::shared_ptr<A> b = make_new<std::shared_ptr<A>>();
std::cout << b->a << std::endl;

现在,对于共享指针,我将实现make_new,如下所示,

template<typename P>
auto make_new()
{
    using Ptype = typename P::element_type;
    return P(new Ptype);
}

不适用于指针。

现在,既适用于指针又适用于智能指针的东西

template<typename P>
auto make_new()
{
    using Ptype = typename std::remove_reference<decltype(*P())>::type;
    return P(new Ptype);
}

但不适用于std::optional

是否存在获取可取消引用对象的基础类型的规范方法?

我知道*->可以重载到任何东西,并且不能保证构造函数可以像上面那样工作或有意义。

只想知道是否有办法,而不仅仅是找到它,或者只是做一些愚蠢的事情。

1 个答案:

答案 0 :(得分:5)

解析指针和类上的元素类型

目标。我们的目标是编写一个using模板,该模板采用可取消引用的类型作为输入,并返回元素类型。

template<class T>
using element_type_t = /* stuff */;

方法。。我们可以使用SFINAE来检查是否存在element_type属性,如果没有,则退回到使用std::remove_reference<decltype(*P())>()

// This is fine to use in an unevaluated context
template<class T>
T& reference_to(); 

// This one is the preferred one
template<class Container>
auto element_type(int) 
  -> typename Container::element_type;

// This one is the fallback if the preferred one doesn't work
template<class Container>
auto element_type(short) 
  -> typename std::remove_reference<decltype(*reference_to<Container>())>::type;

一旦有了此功能,我们就可以通过获取返回类型element_type_t来编写element_type

// We alias the return type
template<class T>
using element_type_t = decltype(element_type<T>(0)); 

为什么我们不能总是通过取消引用来获取element_type?如果您尝试始终使用*运算符获取值类型,则可能导致诸如迭代器之类的问题用于std::vector<bool>,它返回一个行为类似于bool,但封装了位操作的对象。在这些情况下,元素类型与通过取消引用返回的类型不同。

确定构造函数是使用指针还是值

您的代码因std::optional而失败的原因是因为std::optional的构造函数本身使用值,而不是指向该值的指针。 为了确定我们需要哪个构造函数,我们再次使用SFINAE进行确定。

// Base case - use new operator
template<class Container>
auto make_new_impl(int) 
    -> decltype(Container{new element_type_t<Container>})
{
    return Container{new element_type_t<Container>};
}

// Fallback case, where Container takes a value
template<class Container>
auto make_new_impl(long)
    -> decltype(Container{element_type_t<Container>()})
{
    return Container{element_type_t<Container>()};
}

现在,我们可以编写make_new,以便它调用make_new_impl

template<class Container>
auto make_new() {
    return make_new_impl<Container>(0);
}

示例。我们现在可以使用make_new来制作std::optionalstd::shared_ptr甚至是常规指针。

#include <optional>
#include <memory>

int main() {
    // This works
    int* ptr = make_new<int*>(); 

    // This works too
    std::shared_ptr<int> s = make_new<std::shared_ptr<int>>();

    // This also works
    std::optional<int> o = make_new<std::optional<int>>(); 
}