2016-09-08 6 views
1

(Этот вопрос был существенно отредактирован от оригинала, не изменяя реальное намерение первоначального вопроса)Автоматически определить подходящий тип, достаточно большой и достаточно точный, чтобы удерживать сумму всех элементов в контейнере

Если сложить все элементы в vector<int>, то ответ может перелиться, требуя что-то вроде intmax_t хранить ответ точно и без перелива. Но intmax_t не подходит для vector<double>.

Я мог бы вручную указать типы:

template<typename> 
struct sum_traits; 

template<> 
struct sum_traits<int> { 
    typedef long accumulate_safely_t; 
}; 

, а затем использовать их следующим образом:

template<typename C> 
auto sum(const C& c) { 
    sum_traits<decltype(c.begin())> :: accumulate_safely_t> r = 0; 
    for(auto &el: c) 
     r += el; 
    return r; 
} 

Мои вопросы: Можно ли автоматически определить подходящий тип, большой и точный тип , поэтому мне не нужно вручную указывать каждый из них по типу?

+2

Вы знаете, 'r' будет в Int, так почему бы не использовать это? –

+0

возможно в C++ 14. – Jarod42

+0

, потому что тип элемента контейнера может быть другим, и результат может быть больше значения, которое может хранить этот тип – Cfon

ответ

1

Вы можете использовать return type deduction в C++ 14 так же, как это:

template<typename C> 
auto sum(const C& c) { 
    auto r = 0; 
    for(auto &el: c) 
     r += el; 
    return r; 
} 

в C++ 11, учитывая ваш C++ 98 кода, вы можете использовать следующее:

template<typename C> 
auto sum(const C& c) -> typename C::value_type { 
    auto r = 0; 
    for(auto &el: c) 
     r += el; 
    return r; 
} 

Но, как было отмечено в комментариях, auto r = 0; будет по-прежнему ресо lve до int во время компиляции. Как предлагается в другой ответ, вы можете захотеть сделать начальный тип значения (и поэтому тип возвращаемого значения) параметра шаблона, а также:

template<typename C, typename T> 
T sum(const C& c, T init) { 
    for(auto &el: c) 
     init += el; 
    return init; 
} 

// usage 

std::vector<std::string> v({"Hello ", "World ", "!!!"}); 
std::cout << sum(v, std::string{}); 
+1

Но, как упоминалось в Gill Bates, версия C++ 14 возвращает 'int' вопреки вашей версии C++ 11. – Jarod42

+0

'auto r = 0;' все равно будет разрешено 'int' во время компиляции. –

+2

BTW, 'typename C :: value_type' меньше, чем' decltype (typename C :: value_type()) 'и работает для нестандартного конструктивного типа. – Jarod42

4

Основная проблема с вашим кодом в том, что auto r = 0 эквивалентен int r = 0. Это не так, как ваш код на C++ 98 работал. В общем, вы не можете найти идеальный тип цели. Ваш код просто вариант std::accumulate, поэтому мы можем посмотреть на то, как стандарт решить эту проблему: она позволяет передавать в начальные значения для аккумулятора, но и его тип: long sum = std::accumulate(begin, end, long{0});

+0

Спасибо! но можно сделать это с помощью auto и decltype в C++ 11? – Cfon

+2

@Cfon: В этом проблема: do ** what **? Каким должен быть тип возврата? Компилятор не психический, и мы тоже. В частности, каково должно быть возвращаемое значение, когда контейнер пуст? И он должен работать, когда контейнер является 'std :: vector '? С 'std :: accumulate' мы можем передать' std :: string {} 'в качестве начального значения. – MSalters

3

Дано:

Если сложить все элементы в векторе, тогда ответ может переполняться, требуя чего-то вроде intmax_t для аккуратного хранения ответа и без переполнения.

Вопрос:

Мои вопросы: Можно ли автоматически определить подходящий тип, большой и точный тип, поэтому я не придется вручную указать каждый из них с помощью типа признака?

Проблема заключается в том, что вы хотите взять данные времени выполнения (вектор) и из нее вывести тип (время компиляции).

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

Единственная информация, которую мы имеем во время компиляции (если вы не указали больше), составляет std::numeric_limits<int>::max() и std::numeric_limits<std::vector<int>::size_type>::max().

У вас даже нет std::vector<int>::max_size() на этом этапе, поскольку он не обязан быть constexpr. Ни вы можете рассчитывать на std::vector<int>::allocator_type::max_size(), потому что это:

  1. функция член
  2. опция
  3. осуждается в C++ 17

Так что мы остались с это максимально возможная сумма :

std::numeric_limits<int>::max() * std::numeric_limits<std::vector<int>::size_type>::max()

мы могли теперь использовать компиляции тим e, чтобы найти подходящее целое число (если такое целое число существует) (что-то связано с std::conditional)

Это не делает тип адаптированным к условиям выполнения, но он, по крайней мере, будет адаптироваться к архитектуре, для которой вы компилируете ,

Что-то вроде этого:

template <bool Signed, unsigned long long NofBits> 
struct smallest_integer 
{ 
    template<std::size_t Bits, class...Candidates> 
    struct select_candidate; 

    template<std::size_t Bits, class...Candidates> 
    using select_candidate_t = typename select_candidate<Bits, Candidates...>::type; 

    template<std::size_t Bits, class Candidate, class...Rest> 
    struct select_candidate<Bits, Candidate, Rest...> 
    { 
     using type = std::conditional_t<std::numeric_limits<Candidate>::digits >= Bits, Candidate, select_candidate_t<Bits, Rest...>>; 
    }; 

    template<std::size_t Bits, class Candidate> 
    struct select_candidate<Bits, Candidate> 
    { 
     using type = std::conditional_t<std::numeric_limits<Candidate>::digits >= Bits, Candidate, void>; 
    }; 

    using type = 
    std::conditional_t<Signed, 
    select_candidate_t<NofBits, std::int8_t, std::int16_t, std::int32_t, std::int64_t, __int128_t>, 
    select_candidate_t<NofBits, std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t, __uint128_t>>; 
}; 

template<bool Signed, unsigned long long NofBits> using smallest_integer_t = typename smallest_integer<Signed, NofBits>::type; 

template<class L, class R> 
struct result_of_multiply 
{ 
    static constexpr auto lbits = std::numeric_limits<L>::digits; 
    static constexpr auto rbits = std::numeric_limits<R>::digits; 
    static constexpr auto is_signed = std::numeric_limits<L>::is_signed or std::numeric_limits<R>::is_signed; 
    static constexpr auto result_bits = lbits + rbits; 

    using type = smallest_integer_t<is_signed, result_bits>; 
}; 

template<class L, class R> using result_of_multiply_t = typename result_of_multiply<L, R>::type; 

struct safe_multiply 
{ 
    template<class L, class R> 
    auto operator()(L const& l, R const& r) const -> result_of_multiply_t<L, R> 
    { 
     return result_of_multiply_t<L, R>(l) * result_of_multiply_t<L, R>(r); 
    } 
}; 

template<class T> 
auto accumulate_values(const std::vector<T>& v) 
{ 
    using result_type = result_of_multiply_t<T, decltype(std::declval<std::vector<T>>().max_size())>; 
    return std::accumulate(v.begin(), v.end(), result_type(0), std::plus<>()); 
} 

struct uint128_t_printer 
{ 

    std::ostream& operator()(std::ostream& os) const 
    { 
     auto n = n_; 
     if (n == 0) return os << '0'; 

     char str[40] = {0}; // log10(1 << 128) + '\0' 
     char *s = str + sizeof(str) - 1; // start at the end 
     while (n != 0) { 

      *--s = ""[n % 10]; // save last digit 
      n /= 10;      // drop it 
     } 
     return os << s; 
    } 
    __uint128_t n_; 
}; 
std::ostream& operator<<(std::ostream& os, const uint128_t_printer& p) 
{ 
    return p(os); 
} 

auto output(__uint128_t n) 
{ 
    return uint128_t_printer{n}; 
} 

int main() 
{ 
    using rtype = result_of_multiply<std::size_t, unsigned>; 
    std::cout << rtype::is_signed << std::endl; 
    std::cout << rtype::lbits << std::endl; 
    std::cout << rtype::rbits << std::endl; 
    std::cout << rtype::result_bits << std::endl; 
    std::cout << std::numeric_limits<rtype::type>::digits << std::endl; 

    std::vector<int> v { 1, 2, 3, 4, 5, 6 }; 
    auto z = accumulate_values(v); 
    std::cout << output(z) << std::endl; 


    auto i = safe_multiply()(std::numeric_limits<unsigned>::max(), std::numeric_limits<unsigned>::max()); 
    std::cout << i << std::endl; 
} 
+0

Спасибо! Буду иметь ввиду :) – Cfon

 Смежные вопросы

  • Нет связанных вопросов^_^