【问题标题】:Constructor for any initializer_list accepted by given T给定 T 接受的任何 initializer_list 的构造函数
【发布时间】:2021-11-02 20:17:13
【问题描述】:

我正在寻找一种解决方案来创建接受给定T 接受的所有std::initializer_lists 的构造函数。例如:

#include <initializer_list>
#include <utility>

template <typename T> struct AllInits
{
  AllInits(std::initializer_list<?>){}
};

struct ManyListTypes
{
  ManyListTypes(std::initializer_list<bool>){}
  ManyListTypes(std::initializer_list<int>){}
  ManyListTypes(std::initializer_list<std::pair<int, int>>){}
};

int main()
{
  AllInits<ManyListTypes> first{true,false,true};
  AllInits<ManyListTypes> second{1,2,3};
  AllInits<ManyListTypes> third{{1,1},{2,2},{3,3}};
  return 0;
}

有可能吗?

【问题讨论】:

  • template &lt;typename U&gt; AllInits(std::initializer_list&lt;U&gt;)?
  • @S.M.: {1, 1} 没有类型。
  • @SM: From list_initialization#Notes "braced-init-list 不是表达式,因此没有类型,例如 decltype({1,2}) 格式不正确。"type deduction using the keyword auto 有一个特殊例外,它在复制列表初始化中将任何花括号初始化列表推导出为 std::initializer_list。”
  • 可以手动使用指定类型吗? template &lt;typename T, typename U&gt; struct AllInits { AllInits(std::initializer_list&lt;U&gt;){} }; AllInits&lt;ManyListTypes, bool&gt; first{true,false,true}; AllInits&lt;ManyListTypes, int&gt; second{1,2,3}; AllInits&lt;ManyListTypes, std::pair&lt;int, int&gt;&gt; third{{1,1},{2,2},{3,3}};
  • @wehin19066:继承构造函数是一个选项template &lt;typename T&gt; struct AllInits : T { using T::T; };吗?

标签: c++ generics constructor initializer-list


【解决方案1】:

最好的实用解决方案是:

template <typename T>
struct AllInits
{
    template <typename U>
    requires std::constructible_from<T, std::initializer_list<U> &>
    AllInits(std::initializer_list<U>){}
};

这通过了前两个测试用例,但通过了第三个测试用例。 IE。你不能有嵌套的花括号列表。

一种可能的解决方法是指定元素类型:

AllInits<ManyListTypes> third{std::pair{1,1}, std::pair{2,2}, std::pair{3,3}};

有一个解决方案没有这个限制,但它依赖于一些神秘的技巧,可能不应该在实践中使用。

Run on gcc.godbolt.org

#include <concepts>
#include <cstddef>
#include <initializer_list>
#include <iostream>
#include <type_traits>
#include <utility>

// Some helpers.

template <typename T>
struct Tag
{
    using type = T;
};

template <typename ...P>
struct TypeList
{
    inline static constexpr std::size_t size = sizeof...(P);
};

template <typename T>
void PrintType()
{
    #ifndef _MSC_VER
    std::cout << __PRETTY_FUNCTION__ << '\n';
    #else
    std::cout << __FUNCSIG__ << '\n';
    #endif
};

// A generic stateful-template list.

constexpr void adl_ListElem() {} // A dummy ADL target.

template <typename Key, std::size_t Index>
struct ElemViewer
{
    #if defined(__GNUC__) && !defined(__clang__)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wnon-template-friend"
    #endif
    friend constexpr auto adl_ListElem(ElemViewer);
    #if defined(__GNUC__) && !defined(__clang__)
    #pragma GCC diagnostic pop
    #endif
};

template <typename Key, std::size_t Index, typename Value>
struct ElemWriter
{
    friend constexpr auto adl_ListElem(ElemViewer<Key, Index>) {return Tag<Value>{};}
};

template <typename Key, typename Unique, std::size_t Index = 0, typename = void>
struct NumElems : std::integral_constant<std::size_t, Index> {};

template <typename Key, typename Unique, std::size_t Index>
struct NumElems<Key, Unique, Index, decltype(adl_ListElem(ElemViewer<Key, Index>{}), void())> : std::integral_constant<std::size_t, NumElems<Key, Unique, Index+1, void>::value> {};

template <typename Key, typename Value>
struct ElemInserter : ElemWriter<Key, NumElems<Key, Value>::value, Value>, std::true_type {};

// Constructor detection.

template <typename T>
struct AnyInitList
{
    template <
        typename U,
        typename = std::enable_if_t<std::is_same_v<U, std::initializer_list<typename U::value_type>>>,
        typename = std::enable_if_t<ElemInserter<T, typename U::value_type>::value>
    >
    operator U() const
    {
        return {};
    }
};

template <typename T, typename = void>
struct DetectCtors {};

template <typename T>
struct DetectCtors<T, decltype(T(AnyInitList<T>{}), void())> {};

template <typename Type, typename I>
struct ElemListLow {};

template <typename T, std::size_t ...I>
struct ElemListLow<T, std::index_sequence<I...>>
{
    static constexpr TypeList<typename decltype(adl_ListElem(ElemViewer<T, I>{}))::type...> helper() {}
    using type = decltype(helper());
};

template <typename T>
struct ElemList : ElemListLow<T, std::make_index_sequence<NumElems<T, decltype(DetectCtors<T>{}, void())>::value>> {};

// Generating a type with the suitable constructors.

template <typename T>
struct SingleCtor
{
    SingleCtor() {}
    SingleCtor(std::initializer_list<T>) {}
};

template <typename T, typename>
struct MultiCtor;

template <typename T, typename ...P>
struct MultiCtor<T, TypeList<P...>> : SingleCtor<P>...
{
    using SingleCtor<P>::SingleCtor...;

    using list = TypeList<P...>;
    
    template <typename U>
    requires std::constructible_from<T, std::initializer_list<U> &>
    MultiCtor(std::initializer_list<U>){}
};

// The final type.

template <typename T>
struct Foo : MultiCtor<T, typename ElemList<T>::type>
{
    using MultiCtor<T, typename ElemList<T>::type>::MultiCtor;
};

// Demo.

struct ManyListTypes
{
    ManyListTypes(std::initializer_list<bool>){}
    ManyListTypes(std::initializer_list<int>){}
    ManyListTypes(std::initializer_list<std::pair<int, int>>){}
};

int main()
{
    PrintType<Foo<ManyListTypes>::list>(); // TypeList<bool, int, std::pair<int, int>>

    Foo<ManyListTypes> first{true,false,true};
    Foo<ManyListTypes> second{1,2,3};
    Foo<ManyListTypes> third{{1,1},{2,2},{3,3}};
}

这里发生了很多事情。

  • class AnyInitList 具有到 std::initializer_list&lt;??&gt; 的模板转换运算符。
  • 尝试从 AnyInitList 构造您的类型 (ManyListTypes),但我们不关心它是否成功,任何错误都会被 SFINAE 隐藏。
  • 此过程为ManyListTypes 的构造函数可以接受的所有类型实例化AnyInitList。然后,我们使用有状态元编程将这些类型收集到一个列表中。
  • 剩下的很简单。我们创建可从std::initializer_list&lt;T&gt; 构造的class SingleCtor&lt;T&gt;,然后从SingleCtor(继承构造函数)的几个特化中继承最终类型,我们得到的列表中的每种类型都有一个。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2016-10-07
  • 1970-01-01
  • 2012-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-31
相关资源
最近更新 更多