diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx
index b1352a379..4dfa3492a 100644
--- a/src/CommandLine.cxx
+++ b/src/CommandLine.cxx
@@ -139,7 +139,7 @@ static void version()
 		   "\n"
 		   "Decoder plugins:\n");
 
-	decoder_plugins_for_each([](const DecoderPlugin &plugin){
+	for (const DecoderPlugin &plugin : GetAllDecoderPlugins()) {
 		fmt::print(" [{}]", plugin.name);
 
 		const char *const*suffixes = plugin.suffixes;
@@ -156,7 +156,7 @@ static void version()
 				fmt::print(" {}", i);
 
 		fmt::print("\n");
-	});
+	}
 
 	fmt::print("\n"
 		   "Filters:\n"
diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx
index ebf37681d..e6deef145 100644
--- a/src/decoder/DecoderList.cxx
+++ b/src/decoder/DecoderList.cxx
@@ -157,9 +157,8 @@ decoder_plugin_init_all(const ConfigData &config)
 void
 decoder_plugin_deinit_all() noexcept
 {
-	decoder_plugins_for_each_enabled([=](const DecoderPlugin &plugin){
-			plugin.Finish();
-		});
+	for (const auto &plugin : GetEnabledDecoderPlugins())
+		plugin.Finish();
 }
 
 bool
diff --git a/src/decoder/DecoderList.hxx b/src/decoder/DecoderList.hxx
index 7fca3888b..2e0a39f3b 100644
--- a/src/decoder/DecoderList.hxx
+++ b/src/decoder/DecoderList.hxx
@@ -1,8 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 // Copyright The Music Player Daemon Project
 
-#ifndef MPD_DECODER_LIST_HXX
-#define MPD_DECODER_LIST_HXX
+#pragma once
+
+#include "util/DereferenceIterator.hxx"
+#include "util/FilteredContainer.hxx"
+#include "util/TerminatedArray.hxx"
 
 #include <string_view>
 
@@ -37,13 +40,26 @@ public:
 	}
 };
 
+static inline auto
+GetAllDecoderPlugins() noexcept
+{
+	return DereferenceContainerAdapter{TerminatedArray<const DecoderPlugin *const, nullptr>{decoder_plugins}};
+}
+
+static inline auto
+GetEnabledDecoderPlugins() noexcept
+{
+	const auto all = GetAllDecoderPlugins();
+	return FilteredContainer{all.begin(), all.end(), decoder_plugins_enabled};
+}
+
 template<typename F>
 static inline const DecoderPlugin *
 decoder_plugins_find(F f) noexcept
 {
-	for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i)
-		if (decoder_plugins_enabled[i] && f(*decoder_plugins[i]))
-			return decoder_plugins[i];
+	for (const auto &plugin : GetEnabledDecoderPlugins())
+		if (f(plugin))
+			return &plugin;
 
 	return nullptr;
 }
@@ -52,30 +68,13 @@ template<typename F>
 static inline bool
 decoder_plugins_try(F f)
 {
-	for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i)
-		if (decoder_plugins_enabled[i] && f(*decoder_plugins[i]))
+	for (const auto &plugin : GetEnabledDecoderPlugins())
+		if (f(plugin))
 			return true;
 
 	return false;
 }
 
-template<typename F>
-static inline void
-decoder_plugins_for_each(F f)
-{
-	for (auto i = decoder_plugins; *i != nullptr; ++i)
-		f(**i);
-}
-
-template<typename F>
-static inline void
-decoder_plugins_for_each_enabled(F f)
-{
-	for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i)
-		if (decoder_plugins_enabled[i])
-			f(*decoder_plugins[i]);
-}
-
 /**
  * Is there at least once #DecoderPlugin that supports the specified
  * file name suffix?
@@ -83,5 +82,3 @@ decoder_plugins_for_each_enabled(F f)
 [[gnu::pure]]
 bool
 decoder_plugins_supports_suffix(std::string_view suffix) noexcept;
-
-#endif
diff --git a/src/decoder/DecoderPrint.cxx b/src/decoder/DecoderPrint.cxx
index 81f016b21..8015ab145 100644
--- a/src/decoder/DecoderPrint.cxx
+++ b/src/decoder/DecoderPrint.cxx
@@ -37,7 +37,6 @@ decoder_plugin_print(Response &r,
 void
 decoder_list_print(Response &r)
 {
-	const auto f = [&](const auto &plugin)
-		{ return decoder_plugin_print(r, plugin); };
-	decoder_plugins_for_each_enabled(f);
+	for (const auto &plugin : GetEnabledDecoderPlugins())
+		decoder_plugin_print(r, plugin);
 }
diff --git a/src/ls.cxx b/src/ls.cxx
index f15476ca2..6f2961695 100644
--- a/src/ls.cxx
+++ b/src/ls.cxx
@@ -26,10 +26,10 @@ void print_supported_uri_schemes_to_fp(FILE *fp)
 			protocols.emplace(uri);
 		});
 
-	decoder_plugins_for_each([&protocols](const auto &plugin){
+	for (const DecoderPlugin &plugin : GetAllDecoderPlugins()) {
 		if (plugin.protocols != nullptr)
 			protocols.merge(plugin.protocols());
-	});
+	};
 
 	for (const auto& protocol : protocols) {
 		fmt::print(fp, " {}", protocol);
@@ -46,10 +46,10 @@ print_supported_uri_schemes(Response &r)
 			protocols.emplace(uri);
 		});
 
-	decoder_plugins_for_each_enabled([&protocols](const auto &plugin){
+	for (const auto &plugin : GetEnabledDecoderPlugins()) {
 		if (plugin.protocols != nullptr)
 			protocols.merge(plugin.protocols());
-	});
+	}
 
 	for (const auto& protocol : protocols) {
 		r.Fmt(FMT_STRING("handler: {}\n"), protocol);