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. foo
artı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 )
value
Geri 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 */
}) });
result
ve 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::function
bunun için sıfır yükü olmayan tahsisatsız ortamlarda yapacağımız için biraz ağır. Bu yüzden function_view
asla 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 foo
yerine, 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 variant
vec ++ 20routinler, foo
dö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 foo
zaman 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_source
bunu sağlamak için tamsayılar vardır. Ne zaman yapar int_dest1
ve int_dest2
sonuçları almaya başlayın.