【问题标题】:Get underlying type of any C++ dereferencable type获取任何 C++ 可解引用类型的基础类型
【发布时间】:2019-05-14 17:32:38
【问题描述】:

我有一个函数可以创建一个基础类型为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

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

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

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

【问题讨论】:

  • 无关:如果这不是出于教育目的,consider using std::make_shared 和朋友。如果这是出于教育目的,您可以通过查看 std::make_shared 的实现方式来找到一些巧妙的技巧。
  • 您可以使用std::declval&lt;P&gt;() 代替P() 中的decltype。它适用于非默认可构造类型。
  • 也许通过专业化?虽然你为什么不使用类似于std::make_sharedstd::make_unique 的东西并且not 使用指针类型作为模板参数?我也很好奇你试图解决的潜在问题?为什么需要这个make_new 函数?为什么需要将模板参数设为可取消引用的类型?
  • 你试过std::remove_pointer吗?
  • @Someprogrammerdude std::remove_pointer 不会对 shared_ptr 做任何事情。

标签: c++ pointers templates


【解决方案1】:

解析指针和类的元素类型

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

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

方法。我们可以使用 SFINAE 来检查是否有 element_type 属性,如果没有,我们就回退到使用 std::remove_reference&lt;decltype(*P())&gt;()

// 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的返回类型来编写element_type_t

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

为什么我们不能总是通过取消引用来获取 element_type? 如果您尝试始终使用 * 运算符获取值类型,这可能会导致诸如 @ 的迭代器之类的问题987654330@,它返回一个像 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>>(); 
}

【讨论】:

  • 谢谢。绝对精彩。没想到我会这样做。
猜你喜欢
  • 2011-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 2010-10-25
  • 1970-01-01
相关资源
最近更新 更多