Дано:
Если сложить все элементы в векторе, тогда ответ может переполняться, требуя чего-то вроде 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()
, потому что это:
- функция член
- опция
- осуждается в 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;
}
Вы знаете, 'r' будет в Int, так почему бы не использовать это? –
возможно в C++ 14. – Jarod42
, потому что тип элемента контейнера может быть другим, и результат может быть больше значения, которое может хранить этот тип – Cfon