• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed [C++] Wygodna funkcja do losowania dowolnego typu liczb

Object Storage Arubacloud
+2 głosów
643 wizyt
pytanie zadane 13 lipca 2017 w C i C++ przez niezalogowany
zamknięte 17 grudnia 2017

Chciałbym zrobić wygodną funkcję do losowania dowolnego typu liczb. Bez konieczności deklarowania generatora pseudolosowego, maksymalnie wygodne.

#include <exception>
#include <iostream>
#include <random>
#include <type_traits>

template<typename T>
T random(T begin, T end)
{
	static std::random_device rd;
	static std::mt19937 gen(rd());

	if (std::is_floating_point<T>::value)
	{
		std::uniform_real_distribution<T> dis(begin, end);
		return dis(gen);
	}
	else if (std::is_integral<T>::value)
	{
		std::uniform_int_distribution<T> dis(begin, end);
		return dis(gen);
	}
	else throw std::invalid_argument("Function 'random' must have a numeric arguments");
}

int main()
{
	std::cout << std::fixed;

	typedef float type;
	type a = 0;
	type b = 1;

	for (size_t i = 0; i < 10; i++)
	{
		std::cout << random(a, b) << "\n";
	}
	
	std::cin.get();
}

Wszystko w miarę dobrze działało w momencie gdy nie przesyłałem typu jakiego chcę otrzymać:

std::uniform_real_distribution<> dis(begin, end);
std::uniform_int_distribution<> dis(begin, end);

Kod błędów:

error C2338: make_signed<T>/make_unsigned<T> require that T shall be a (possibly cv-qualified) integral type or enumeration but not a bool type.
error C2338: invalid template argument for uniform_int_distribution: N4606 26.6.1.1 [rand.req.genl]/1e requires one of short, int, long, long long, unsigned short, unsigned int, unsigned long, or unsigned long long

Czy dałoby się to naprawić? Czy może droga, którą podążam jest zła i może mógłbym to zrobić w inny sposób?

komentarz zamknięcia: odpowiedz uzyskana

3 odpowiedzi

+1 głos
odpowiedź 13 lipca 2017 przez mokrowski Mędrzec (155,460 p.)
wybrane 14 lipca 2017
 
Najlepsza

#include <iostream>
#include <random>
#include <type_traits>
#include <iomanip>

template<typename T>
T random_in_range(T begin, T end)
{
    using rand_distrib = typename std::conditional
    <
        std::is_floating_point<T>::value,
        std::uniform_real_distribution<T>,
        std::uniform_int_distribution<T>
    >::type;

    static std::random_device rd;
    static std::mt19937_64 gen(rd());

    rand_distrib dis(begin, end);
    return dis(gen);
}
 
int main()
{
    std::cout << std::setprecision(20) << std::fixed;

    using type_t = long double; 
    type_t a = 0;
    type_t b = 1;
 
    for (auto i = 0; i < 10; ++i)
    {
        std::cout << random_in_range(a, b) << "\n";
    }
}

O coś takiego Ci chodzło?

komentarz 14 lipca 2017 przez criss Mędrzec (172,590 p.)
O a więc jest w std takie coś :D std::conditional
komentarz 14 lipca 2017 przez mokrowski Mędrzec (155,460 p.)
No jest. IMHO implementacja przez specjalizacje szablonów robiła by duży narzut kodu a konkretyzacja uniform_int_distribution i tak nie powiedzie się dla "niekompatybilnego" typu więc nie ma co się silić na statyczne asercje.
komentarz 14 lipca 2017 przez niezalogowany
edycja 14 lipca 2017
Dokładnie o to chodziło! Dziękuję :)

Nawet przemyślałeś użycie 64-bitowego generatora jestem pod wrażeniem.
+3 głosów
odpowiedź 13 lipca 2017 przez adrian17 Ekspert (344,860 p.)
edycja 14 lipca 2017 przez adrian17
Szablon generuje obie strony ifa (nawet te niespełnione), a X_distribution sprawdza poprawność typów w momencie kompilacji - dlatego się wywala.

Co ciekawe, w C++17 ten kod byłby poprawny i można by go użyć w tej formie - wystarczyłoby zamienić `if (std::...)` na `if constexpr (std::...)`, który w momencie kompilacji kompiluje tylko tą stronę if/else, która jest prawdziwa (kompilujący się przykład: https://godbolt.org/g/R8kbWK ).

A jak na razie, to "poprawnym" rozwiązaniem będzie zastąpienie if/else specjalizacjami funkcji. Można przez specjalizację na boolu, tag dispatch lub użycie SFINAE (enable_if) (zakładam że doguglujesz te tematy, jakby coś mogę dalej podpowiadać)
komentarz 14 lipca 2017 przez niezalogowany
Okazało się, że bez `if constexpr` też da się coś takiego zrobić, ale dzięki za odpowiedź i wyjaśnienie problemu ;)
komentarz 14 lipca 2017 przez adrian17 Ekspert (344,860 p.)

Okazało się, że bez `if constexpr` też da się coś takiego zrobić

(No tak, w trzecim akapicie napisałem jak to można zrobić. Criss dał konkretny przykład.)

1
komentarz 14 lipca 2017 przez niezalogowany
(Wybacz źle się wysłowiłem - miałem na myśli - "Okazało się, że bez `if constexpr` też da się coś takiego zrobić tak jak mówiłeś". Criss dostał łapkę.)
0 głosów
odpowiedź 13 lipca 2017 przez criss Mędrzec (172,590 p.)

Dopowiadając do odpowiedzi adriana:
Możesz sam sobie w prosty sposób napisać coś w rodzaju constexpr if-a:

template<typename T, bool IsFloatingPoint = std::is_floating_point<T>::value>
struct TypeDependentDist;

template<typename T>
struct TypeDependentDist<T, false> { using Type = std::uniform_int_distribution<T>; };

template<typename T>
struct TypeDependentDist<T, true> { using Type = std::uniform_real_distribution<T>; };
 
int main()
{
    std::mt19937 gen;
    std::cout << TypeDependentDist<float>::Type{1, 10}(gen) << '\n';
    std::cout << TypeDependentDist<int>::Type{1, 10}(gen);
}

 

Podobne pytania

+1 głos
2 odpowiedzi 1,338 wizyt
0 głosów
1 odpowiedź 647 wizyt
0 głosów
1 odpowiedź 4,799 wizyt

92,575 zapytań

141,424 odpowiedzi

319,649 komentarzy

61,960 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...