Если у вас есть доступ к последней версии boost, вы можете использовать невероятно мощную библиотеку HOF (расшифровывается как функции более высокого порядка).
Одной из функций, которые я чаще всего использую для упрощения выбора пути кода на основе типа аргумента, является функция first_of
.
Способ, которым это работает, заключается в том, что вы предоставляете ему список объектов шаблонных функций (или лямбд) в том порядке, в котором вы хотите, чтобы компилятор их пробовал. Выбран первый объект легальной функции в списке.
пример:
#include <cstddef>
#include <boost/hof.hpp>
#include <cstring>
#include <utility>
#include <iostream>
// a function to compute length from a pointer. For exposition,
// I have only considered char pointers but any number of overloads will work.
template<class T>
std::size_t
string_pointer_length(T*p)
{
// for exposition
return std::strlen(p);
}
// a function to compute string length from a literal
template<class T, std::size_t N>
constexpr
std::size_t literal_string_length(T (&s)[N])
{
return N - 1;
}
// The generic GetLength function which takes any kind of string
template <typename T>
std::size_t GetLength(T&& str)
{
// select the FIRST legal choice of the following lambdas and invoke...
return boost::hof::first_of(
[](auto&&s) BOOST_HOF_RETURNS(literal_string_length(s)),
[](auto&&s) BOOST_HOF_RETURNS(string_pointer_length(s))
)(str);
}
int main()
{
static const auto lit = "hello";
auto plit = std::addressof(lit[0]);
auto n = GetLength(lit);
auto n2 = GetLength(plit);
std::cout << n << ", " << n2 << std::endl;
}
Макрос BOOST_HOF_RETURNS
избавляет нас от необходимости прописывать лямбды следующим образом:
return boost::hof::first_of(
[](auto&&s) -> decltype(literal_string_length(s)) { return literal_string_length(s); },
[](auto&&s) BOOST_HOF_RETURNS(string_pointer_length(s))
)(str);
Если вы не можете использовать boost.hof, написание нашей собственной замены на удивление тривиально:
#include <cstddef>
#include <cstring>
#include <tuple>
#include <utility>
#include <iostream>
template<class T>
std::size_t
string_pointer_length(T*p)
{
// for exposition
return std::strlen(p);
}
template<class T, std::size_t N>
constexpr
std::size_t literal_string_length(T (&s)[N])
{
return N - 1;
}
template<class...Args, class This, class...Others>
constexpr auto try_these(std::tuple<Args...> args, This _this, Others...others)
{
if constexpr (std::is_invocable_v<This, Args...>)
{
return std::apply(_this, args);
}
else
{
return try_these(args, others...);
}
}
struct invoke_string_pointer_length
{
template<class S>
constexpr auto operator()(S&& s) const -> decltype(string_pointer_length(s))
{ return string_pointer_length(s); }
};
struct invoke_literal_string_length
{
template<class S>
constexpr auto operator()(S&& s) const -> decltype(literal_string_length(s))
{ return literal_string_length(s); }
};
template <typename T>
std::size_t GetLength(T&& str)
{
return try_these(std::forward_as_tuple(std::forward<T>(str)),
invoke_literal_string_length(),
invoke_string_pointer_length());
}
int main()
{
static const auto lit = "hello";
auto plit = std::addressof(lit[0]);
auto n = GetLength(lit);
auto n2 = GetLength(plit);
std::cout << n << ", " << n2 << std::endl;
}