【问题标题】:how to check if type 'T' has 'T(std::initializer_list<U>)' constructor如何检查类型'T'是否有'T(std::initializer_list<U>)'构造函数
【发布时间】:2019-08-11 21:00:21
【问题描述】:

我有一个函数func &lt;T&gt; (...),它必须分成两个分支;

  • 第一个分支:T 类型具有 T(std::initializer_list&lt;U&gt;) 构造函数的情况。

  • 第二个分支:T 类型没有 T(std::initializer_list&lt;U&gt;) 构造函数的情况。

我目前的实现如下:

template<typename T, typename U>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
    T,
    std::initializer_list<U>
>>;

// version for T with initialization list ctor
template<
    typename T,
    typename = std::enable_if_t<
        std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
    >
>
void func() {
    //... 
}

// version for T without initialization list ctor
template<
    typename T,
    typename = std::enable_if_t<
        !std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
    >
>
void func() {
    //... 
}

但它有一个缺陷。我不知道如何从T 自动推断类型U

理想的解决方案是:

template<typename T>
struct deduce_U_from_T
{
    // implementation.

    usign type = /* ??? */;
};

template<typename T>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
    T,
    std::initializer_list<
        typename deduce_U_from_T<T>::type
    >
>>;

但我不知道如何实现deduce_U_from_T

有什么办法可以解决这个问题吗? 或者有什么解决方法吗?


更新:

函数func &lt;T&gt; (...) 是对std::alocator_traits::construct() 的模仿。 我正在尝试实现我自己的“分配器”以使用std::vector 和智能指针。一般情况下,我会使用默认的std::alocator_traits,但是这一次,我需要从“特殊”池中请求内存(这是我自己实现的,可以称为“虚拟堆”,通过方法访问)像T * get_memory &lt;T&gt; (...),池在内存分配期间执行额外的操作,并提供不同的分配“模式” - 很抱歉非常通用,但目前它是 WIP,它会不断变化)

func &lt;T&gt; (...) (allocator_traits::construct()) 的简单实现

template<typename T>
class allocator_traits
{
//...

public:
    template<typename... Args>
    static
    std::enable_if_t<
        std::is_detected_v<has_init_list_ctor, T>,
        void
    > construct(T * ptr, Args && ... args)
    {
        new(ptr) T(std::forward<Args>(args)...); // normal brackets // construct with placment-new
    }

    template<typename... Args>
    static
    std::enable_if_t<
        !std::is_detected_v<has_init_list_ctor, T>,
        void
    > construct(T * ptr, Args && ... args)
    {
        new(ptr) T{ std::forward<Args>(args)... }; // curly brackets // construct with placment-new
    }

//...
};

不同之处在于可以使用大括号构造类型 T(当类型 T 没有 T(std::initializer_list&lt;U&gt;) 构造函数时。

【问题讨论】:

  • XY 问题? func() 究竟要做什么,你为什么关心T 是否有一个initializer_list&lt;U&gt; 构造函数用于一些任意的U(而不是一些特定的U)?
  • 这只有在 U 是离散类型时才有可能。如果是必须推导的模板类型,恐怕是不可能的。 C++ 不能以这种方式工作。
  • 如果不出意外,T 可能有多个构造函数将std::initializer_list&lt;U&gt; 用于不同的U。仅出于这个原因,“从T 自动推断类型U”的目标可能是没有希望的。为什么你觉得你需要这个?听起来像XY problem
  • U不是std::common_type_t&lt;Args...&gt;吗?
  • @Jarod42 No. U 甚至可能与 Args... 没有任何关系。考虑vector&lt;char&gt;{1, 2, 3}vector&lt;string&gt;{"a", "bc", "def"}

标签: c++ templates constructor c++17 sfinae


【解决方案1】:

有没有办法解决这个问题?

是的。不要解决它。您不应该试图猜测用户想要什么样的初始化。只需这样做:

new (ptr) T(std::forward<Args>(args)...)

如果用户想要使用initializer_list 构造函数,他们可以传入initializer_list 的实例,这样就可以了。

更有趣的情况是聚合,这就是为什么在 C++20 中它们可以用括号初始化(参见 P0960)。但这可以通过传入一个具有适当转换运算符的参数来解决。也就是说,如果我想构造一个:

struct X { int i; };

并使用括号使其工作,我可以传入一个类型的参数:

struct X_factory { int i; operator X() const { return X{i}; } };

在保证复制省略的情况下,无论如何我们都会得到正确的效果。


无论如何,initializer_list 实际上与问题并没有严格的关系。你可能想要的(我不建议这样做)是:

if constexpr (std::is_constructible_v<T, Args...>) {
    new (ptr) T(std::forward<Args>(args)...);
} else {
    new (ptr) T{std::forward<Args>(args)...};
}

或者可能以相反的顺序为直接列表初始化写一个特征。

【讨论】:

  • 你能详细说明一下工厂应该如何使用吗?
【解决方案2】:

@Barry 是对的,这是个坏主意。

这是怎么做的:

#include <initializer_list>
#include <utility>
#include <iostream>

struct any_il{
    template<class U>
    operator std::initializer_list<U>()const{ return {}; }
};

struct foo {
    foo(std::initializer_list<int>){}
    foo(foo&&)=default;
};

template<class T, class=void>
struct can_from_il:std::false_type{};

template<class T>
struct can_from_il<T, decltype(void( T(any_il{}) ) )>:std::true_type{};

int main(){
    std::cout << can_from_il<foo>{}() << can_from_il<int>{}() <<"\n";
}

它有很多缺陷。

【讨论】:

    猜你喜欢
    • 2014-11-30
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-24
    • 1970-01-01
    相关资源
    最近更新 更多