Есть ли способ устранить эту двусмысленность? - PullRequest
0 голосов
/ 09 мая 2019

Я хотел бы предоставить две формы функции стиля GetLength(psz) - ту, которая не знает верхних границ, и ту, которая имеет:

template <typename T>
size_t GetLength(const T * psz) { /* compute size w/o knowing what upper bound may be */ }

template <typename T, size_t size>
size_t GetLength(const T(&psz)[size]) { /* we know the upper bound */ }

Мне бы хотелось, чтобы это небыть двусмысленнымЯ хочу, чтобы версия размера массива была выбрана, когда аргумент является массивом известного размера.Я хочу, чтобы неограниченная версия была выбрана, когда аргумент является просто указателем, а не известным фиксированным массивом.

Я бы также предложил 3-ю версию, которая явно принимает верхние границы в качестве аргумента, без шаблонного вычета размера, дляпередача этой информации из внешнего контекста, который в противном случае утратил способность выводить ее из своих локальных аргументов.

Есть ли метод, который я могу использовать, чтобы заставить компилятор отказаться от первой версии моей функции?(нет известных границ), когда границы известны?

Ответы [ 3 ]

2 голосов
/ 09 мая 2019

Есть ли метод, который я могу использовать, чтобы заставить компилятор отказаться от 1-й версии моей функции (без известных границ), когда границы известны?

Как насчет добавления уровнякосвенного обращения?

template <typename T>
std::size_t GetLength (const T * psz, int)
 { /* compute size w/o knowing what upper bound may be */ }

template <typename T, size_t size>
std::size_t GetLength (const T(&psz)[size], long)
 { /* we know the upper bound */ }

template <typename T>
std::size_t GetLength (T const & t)
 { GetLength(t, 0L); }

Добавляя неиспользуемый другой параметр (int или long), вы можете выбрать предпочтительную версию.

1 голос
/ 09 мая 2019

Если у вас есть доступ к последней версии 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;
}
1 голос
/ 09 мая 2019

Мы могли бы использовать черты типа:

#include <type_traits>

   // If T is an array 
   template<
       typename T,
       typename std::enable_if <
       std::is_array<T>{},
       size_t
       > ::type Extent = std::extent<T>::value
   >
   size_t GetLength(const T& t)
   {
       return Extent;
   }

   // If T is not an array 
   template<typename T,
       typename std::enable_if <
       !std::is_array<T>{},
       size_t
       > ::type = 0
   >
   size_t GetLength(const T& t)
   {
       return {};
   }

   int main()
   {
       int arr[5]{};
       GetLength(arr); // calls first

       //decay to pointer
       auto val = arr;
       GetLength(val); // calls second
   }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...