Birden çok parametreyi döndürmenin birçok yolu vardır. Ben exhastive olacağım.
Referans parametrelerini kullanın:
void foo( int& result, int& other_result );
işaretçi parametrelerini kullanın:
void foo( int* result, int* other_result );
Bu, &çağrı sitesinde bir yapmanız gereken avantaja sahiptir , muhtemelen insanları dışarı parametresi olarak uyarır.
Bir şablon yazın ve kullanın:
template<class T>
struct out {
std::function<void(T)> target;
out(T* t):target([t](T&& in){ if (t) *t = std::move(in); }) {}
out(std::optional<T>* t):target([t](T&& in){ if (t) t->emplace(std::move(in)); }) {}
out(std::aligned_storage_t<sizeof(T), alignof(T)>* t):
target([t](T&& in){ ::new( (void*)t ) T(std::move(in)); } ) {}
template<class...Args> // TODO: SFINAE enable_if test
void emplace(Args&&...args) {
target( T(std::forward<Args>(args)...) );
}
template<class X> // TODO: SFINAE enable_if test
void operator=(X&&x){ emplace(std::forward<X>(x)); }
template<class...Args> // TODO: SFINAE enable_if test
void operator()(Args...&&args){ emplace(std::forward<Args>(args)...); }
};
o zaman yapabiliriz:
void foo( out<int> result, out<int> other_result )
ve her şey yolunda. fooartık bonus olarak iletilen hiçbir değeri okuyamaz.
Verileri koyabileceğiniz bir noktayı tanımlamanın diğer yolları oluşturmak için kullanılabilir out. Örneğin, bir yerlerde bir şeyleri yerleştirmek için geri arama.
Bir yapıyı iade edebiliriz:
struct foo_r { int result; int other_result; };
foo_r foo();
whick her C ++ sürümünde ve c ++ 17 bu ayrıca aşağıdakilere de izin verir:
auto&&[result, other_result]=foo();
sıfır maliyetle. Parametreler, garantili seçim sayesinde taşınamaz bile.
Biz dönebilir std::tuple:
std::tuple<int, int> foo();
hangi parametreler adlandırılmamış dezavantajı vardır. Bu izin verirc ++ 17:
auto&&[result, other_result]=foo();
de. Öncec ++ 17 bunun yerine şunları yapabiliriz:
int result, other_result;
std::tie(result, other_result) = foo();
ki bu biraz daha garip. Ancak garantili seçim burada işe yaramıyor.
Yabancı bir bölgeye (ve bundan sonra out<>!) Gidip, devam geçiş stilini kullanabiliriz:
void foo( std::function<void(int result, int other_result)> );
ve şimdi arayanlar:
foo( [&](int result, int other_result) {
/* code */
} );
bu stilin bir avantajı, belleği yönetmek zorunda kalmadan rastgele sayıda değer (tek tip tipte) döndürebilmenizdir:
void get_all_values( std::function<void(int)> value )
valueGeri arama yaparken 500 kez sen denilebilir get_all_values( [&](int value){} ).
Saf delilik için, devamında bir süreklilik bile kullanabilirsiniz.
void foo( std::function<void(int, std::function<void(int)>)> result );
kullanımı şöyle:
foo( [&](int result, auto&& other){ other([&](int other){
/* code */
}) });
resultve arasında birden fazla ilişkiye izin verir other.
Yine Kaliforniya değerleriyle bunu yapabiliriz:
void foo( std::function< void(span<int>) > results )
burada geri aramayı bir sonuç aralığı ile çağırıyoruz. Bunu tekrar tekrar bile yapabiliriz.
Bunu kullanarak, yığından herhangi bir ayırma yapmadan megabaytlık verileri verimli bir şekilde ileten bir işleve sahip olabilirsiniz.
void foo( std::function< void(span<int>) > results ) {
int local_buffer[1024];
std::size_t used = 0;
auto send_data=[&]{
if (!used) return;
results({ local_buffer, used });
used = 0;
};
auto add_datum=[&](int x){
local_buffer[used] = x;
++used;
if (used == 1024) send_data();
};
auto add_data=[&](gsl::span<int const> xs) {
for (auto x:xs) add_datum(x);
};
for (int i = 0; i < 7+(1<<20); ++i) {
add_datum(i);
}
send_data(); // any leftover
}
Şimdi, std::functionbunun için sıfır yükü olmayan tahsisatsız ortamlarda yapacağımız için biraz ağır. Bu yüzden function_viewasla ayırmayan bir tane istiyoruz .
Başka bir çözüm:
std::function<void(std::function<void(int result, int other_result)>)> foo(int input);
burada geri çağrıyı alıp çağırmak fooyerine, geri çağrıyı alan bir işlev döndürür.
foo (7) ([&] (int sonucu, int other_result) {/ * kod * /}); bu, ayrı köşeli ayraçlar kullanarak çıkış parametrelerini giriş parametrelerinden ayırır.
İle variantvec ++ 20routinler, foodönüş türlerinin (veya yalnızca dönüş türünün) bir varyantını oluşturabilirsiniz. Sözdizimi henüz düzeltilmedi, bu yüzden örnek vermeyeceğim.
Sinyaller ve yuvalar dünyasında, bir dizi sinyali ortaya çıkaran bir işlev:
template<class...Args>
struct broadcaster;
broadcaster<int, int> foo();
oluşturmanıza izin verir foozaman uyumsuz çalışan ve sonuç bittiğinde sonucu yayınlayan .
Bu çizgide, bir fonksiyonun bir şey yapmadığı, ancak verilerin bir şekilde bağlanmasını düzenlediği ve işin nispeten bağımsız olduğu çeşitli boru hattı tekniklerimiz var.
foo( int_source )( int_dest1, int_dest2 );
o zaman bu kod değil yapmak kadar bir şey int_sourcebunu sağlamak için tamsayılar vardır. Ne zaman yapar int_dest1ve int_dest2sonuçları almaya başlayın.