OpenSiv3D icon indicating copy to clipboard operation
OpenSiv3D copied to clipboard

Array, Grid の推論補助を追加

Open Raclamusi opened this issue 4 months ago • 0 comments

#1307 の修正と、機能追加です。

追加される機能

  • 新たに以下のコンストラクタで CTAD が効くようになります
    • Array(size_type count, const value_type& value, const Allocator& alloc = Allocator{})
    • Array(size_type size, Arg::generator_<Fty> generator)
    • Array(size_type size, Arg::indexedGenerator_<Fty> indexedGenerator)
    • Grid(size_type w, size_type h, const value_type& value)
    • Grid(Size size, const value_type& value)
    • Grid(size_type w, size_type h, Arg::generator_<Fty> generator)
    • Grid(Size size, Arg::generator_<Fty> generator)
    • Grid(size_type w, size_type h, Arg::indexedGenerator_<Fty> indexedGenerator)
    • Grid(Size size, Arg::indexedGenerator_<Fty> indexedGenerator)
  • Issue では const value_type& を引数に取るコンストラクタについて問題提起しましたが、この問題を解決するにあたって Arg::generatorArg::indexedGenerator を考慮する必要があったので、一緒に Arg::generatorArg::indexedGenerator を取るコンストラクタでも CTAD が効くようにしました
  • この PR で Grid g(2, 2, Array<int, Allocator<int>>{}) のようなコードが有効になってしまいます
    • コンストラクタ Grid(size_type w, size_type h, const Array<value_type>& data) の引数 dataGrid::allocator_type にかかわらずデフォルトのアロケータを使用する Array であり、 Array<int, Allocator<int>> などの値は渡せません
    • 変更前はエラーでしたが、変更後は以下のように CTAD が非直感的に働く可能性があります
    • Array<int> a1{ 3, 1, 4, 1 };
      Array<int, Allocator<int>> a2{ 3, 1, 4, 1 };
      Grid g1(2, 2, a1);  // Grid<int> に推論される
      Grid g2(2, 2, a2);  // Grid<Array<int, Allocator<int>>> に推論される(!)
      

実装

  • Array.hpp, Grid.hpp に推論補助を追加しました
  • 追加した推論補助 Array(typename Array<Type, Allocator>::size_type, const Type&, const Allocator& = Allocator{}) -> Array<Type, Allocator> には以下のような問題があったため、 std::is_same_v<typename Allocator::value_type, Type> でこの推論補助を制約しています
    • std::list<int> list{ 3, 1, 4, 1 };
      Allocator<int> alloc;
      Array a(list.begin(), list.end(), alloc);  // Array<std::list<int>::iterator, Allocator<int>> に推論され、コンパイルエラー
      
  • 追加した推論補助 Array(typename Array<Type, Allocator>::size_type, const Type&, const Allocator& = Allocator{}) -> Array<Type, Allocator> および対応する Grid の推論補助には以下のような問題があったため、 not detail::IsNamedParameter_v<Type> でこれらの推論補助を制約しています
    • Array a(3, Arg::generator = [] { return 42; });  // Array<Arg::generator_<...>> に推論される
      Grid g1(2, 2, Arg::generator = [] { return 42; });  // Grid<Arg::generator_<...>> に推論される
      Grid g2(Size{ 2, 2 }, Arg::generator = [] { return 42; });  // Grid<Arg::generator_<...>> に推論される
      
    • detail::IsNamedParameter_v を定義し、~~面倒だったので~~ Arg::generator, Arg::indexedGenerator だけでなくすべての NamedParameter を弾いています
    • detail::IsNamedParameter_v の定義は少し変なところに置いており、適切ではないかもしれません

テストコード

# include <Siv3D.hpp> // Siv3D v0.6.16

struct ArrayIsh
{
	const Array<int, Allocator<int>>& asArray() const;
};

static_assert(requires (
	Array<int, Allocator<int>> a,
	std::vector<int, Allocator<int>> v,
	Allocator<int> alloc)
{
	{ Array(a)            } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(std::move(a)) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(v)            } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(std::move(v)) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(3, 42)        } -> std::same_as<Array<int>>;
	{ Array(3, 42, alloc) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(a.begin(), a.end())        } -> std::same_as<Array<int>>;
	{ Array(a.begin(), a.end(), alloc) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(a)                   } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(a, alloc)            } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(std::move(a))        } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(std::move(a), alloc) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(v)                   } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(v, alloc)            } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(std::move(v))        } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(std::move(v), alloc) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array({ 42, 42, 42 })        } -> std::same_as<Array<int>>;
	{ Array({ 42, 42, 42 }, alloc) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(ArrayIsh{}) } -> std::same_as<Array<int, Allocator<int>>>;
	{ Array(3, Arg::generator = [] { return 42; }) } -> std::same_as<Array<int>>;
	{ Array(3, Arg::indexedGenerator = [](size_t) { return 42; }) } -> std::same_as<Array<int>>;
});

static_assert(requires (
	Grid<int, Allocator<int>> g,
	Array<int> a,
	Allocator<int> alloc)
{
	{ Grid(g)            } -> std::same_as<Grid<int, Allocator<int>>>;
	{ Grid(std::move(g)) } -> std::same_as<Grid<int, Allocator<int>>>;
	{ Grid(2, 2, 42) } -> std::same_as<Grid<int>>;
	{ Grid(Size{ 2, 2 }, 42) } -> std::same_as<Grid<int>>;
	{ Grid(2, 2, a)            } -> std::same_as<Grid<int>>;
	{ Grid(2, 2, std::move(a)) } -> std::same_as<Grid<int>>;
	{ Grid(Size{ 2, 2 }, a)            } -> std::same_as<Grid<int>>;
	{ Grid(Size{ 2, 2 }, std::move(a)) } -> std::same_as<Grid<int>>;
	{ Grid({ { 42, 42 }, { 42, 42 } }) } -> std::same_as<Grid<int>>;
	{ Grid(2, 2, Arg::generator = [] { return 42; })         } -> std::same_as<Grid<int>>;
	{ Grid(Size{ 2, 2 }, Arg::generator = [] { return 42; }) } -> std::same_as<Grid<int>>;
	{ Grid(2, 2, Arg::indexedGenerator = [](Point) { return 42; })         } -> std::same_as<Grid<int>>;
	{ Grid(Size{ 2, 2 }, Arg::indexedGenerator = [](Point) { return 42; }) } -> std::same_as<Grid<int>>;
});

void Main()
{
	while (System::Update())
	{

	}
}

Raclamusi avatar Jun 08 '25 10:06 Raclamusi