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

Moc*_*han 6 c++ templates pointers

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

template<typename P>
auto make_new()
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

现在,对于共享指针,我将实现make_new以下内容,

template<typename P>
auto make_new()
{
    using Ptype = typename P::element_type;
    return P(new Ptype);
}
Run Code Online (Sandbox Code Playgroud)

这不适用于指针。

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

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

但不适用于std::optional

有没有一种规范的方法来获取可取消引用对象的基础类型?

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

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

J. *_*rez 5

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

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

template<class T>
using element_type_t = /* stuff */;
Run Code Online (Sandbox Code Playgroud)

方法。我们可以使用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;
Run Code Online (Sandbox Code Playgroud)

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

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

为什么我们不能总是通过取消引用来获取element_type?如果尝试始终使用*运算符获取值类型,则可能会导致诸如for的迭代器之类的问题,该迭代器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>()};
}
Run Code Online (Sandbox Code Playgroud)

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

template<class Container>
auto make_new() {
    return make_new_impl<Container>(0);
}
Run Code Online (Sandbox Code Playgroud)

例。我们现在可以使用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>>(); 
}
Run Code Online (Sandbox Code Playgroud)