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