2012-10-22 40 views
8

Özel akış denetleyicilerinin daha kolay oluşturulmasını sağlamak için C++ 11 özelliklerini kullanmaya çalışıyorum. Lambda fonksiyonlarını manipülatörler olarak kullanabilirim, fakat std::function<ostream&(ostream&)>'u kullanamam.std :: özel bir akış denetleyicisi olarak işlev

İşte kod aşağı haşlanmış bulunuyor:

g++-4 src/Solve.cpp -c -g -std=c++0x -o src/Solve.o -I/home/ekrohne/minisat 
src/Solve.cpp: In function 'int main(int, char**)': 
src/Solve.cpp:24:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&' 
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ostream:579:5: error: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::function<std::basic_ostream<char>&(std::basic_ostream<char>&)>]' 

Bu neden başarısız oluyor:

#include <iostream> 
#include <functional> 
using namespace std; 

auto lambdaManip = [] (ostream& stream) -> ostream& { 
    stream << "Hello world" << endl; 
}; 
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& { 
    stream << "Hello world" << endl; 
}; 

int main (int argc, char** argv) { 
    cout << lambdaManip; // OK 
    cout << functionManip; // Compiler error 
} 

ikinci cout deyimi aşağıdaki başarısız? Cygwin gcc 4.5.3 kullanıyorum.

Ben soruyorum, verimlilik sorunları nedeniyle, her yerde std::function kullanarak hakkında deli değilim. Ancak, lambda işlevlerini döndüren işlevler yazmak istiyorum ve std::function olmadan nasıl yapılacağını bilmiyorum. Örneğin, aşağıdaki gibi bir şey mükemmel olabilir

auto getAdditionFunctor(); 

auto getAdditionFunctor() { 
    return [] (int x, int y) { return x + y }; 
}; 

... ama açıkçası işe yaramaz. İşe yarayan alternatif bir sözdizimi var mı? Ne olabileceğini hayal edemiyorum, bu yüzden std::function ile sıkışmış olabilirim.

Eğer ikinci soruya bir çözüm bulsaydım, o zaman ilk soru tartışılırdı.


Teşekkür ederiz.

operator<<(ostream&, std::function<ostream&(ostream&)> tanımlanıyor. Bir web sayfasını yanlış okumuştum ve ostream'un bir manipülatör olarak bir operator() olan keyfi bir nesneyi tedavi edecek kadar akıllı olduğu izleniminin altındaydı. Bu konuda yanılmışım. Dahası, basit lambda inşa ettiğim gibi, basit bir eski işlev içine derlenmişti. Aslında, lambda'un basit bir işlev olmadığından emin olmak için değişken yakalama kullanırsam, derleyici başarısız olur. Ayrıca, tanımlanan operator() ile nesneler değildir (varsayılan) manipülatörler olarak tedavi edilir:

class Manipulator { 
    ostream& operator()(ostream& stream) const { 
     return stream << "Hello world" << endl; 
    }; 
} classManip; 

function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& { 
    return stream << "Hello world" << endl; 
}; 

int main (int argc, char** argv) { 
    const string str = "Hello world"; 
    auto lambdaManip = [&] (ostream& stream) -> ostream& { 
     return stream << str << endl;  
    }; 

    cout << classManip;  // Compiler error 
    cout << lambdaManip; // Compiler error 
    cout << functionManip; // Compiler error 
} 

fazla güncelleme:

// Tell ostreams to interpret std::function as a 
// manipulator, wherever it sees one. 
inline ostream& operator<<(
     ostream& stream, 
     const function<ostream& (ostream&)>& manipulator) { 
    return manipulator(stream); 
} 
: bu ile gerçekleştirilebilir Aşağıdakiler göre biraz daha güçlü bir çözüm çıkıyor

Bu kodun bir const vardır. Projemdeki çözümü gerçek anlamda uygulamaya çalışmayı keşfettim. Eğer ostream için operator<< bakarsak

+4

Bu manipülatörlerde bir 'geri dönüş' bırakmak mı istediniz? –

+0

Bunların ima edildiğini ve bunları eklemenin yardımcı olmadığını okudum. Bu durumda, geri dönüşlerle daha okunabilir olduklarını düşünüyorum, ancak bunlar yasal değil. Her şeyden önce, her şey çalışır eğer ben cout << functionManip; –

+3

@EdKrohne: 'return' ** burada ** isteğe bağlı değildir - lambda'larınız, herhangi bir işlevde olduğu gibi, bir“ void ”dönüş türü olmayan ve bir değer döndürmeyen UB'yi çağırır. §6.6.3/2: "* Bir fonksiyonun sonunun akması, bir değeri olmayan bir" dönüş "e eşdeğerdir, bu, bir değer döndüren işlevde tanımlanmamış bir davranışla sonuçlanır. *" İyi çalışmanın görünmesi sadece olası bir tezahürdür. UB. – ildjarn

cevap

7

, bir std::function çekmek için aşırı yüklenme var - ve bu size cout << functionManip burada yapmaya çalıştığımız şey temelde bu.Bu sorunu gidermek için ya aşırı kendiniz tanımlamak için:

ostream& operator<<(ostream& os, std::function<ostream& (ostream&)>& s) 
{ 
    return s(os); 
} 

Veya işlevine argüman olarak stream geçmesi: Verilen, lambda çalışıyor neden gelince

functionManip(std::cout); 

bir dönüş türü lambda tanımlanmamıştır ve bir aşırı yük bir işlev işaretçisi kullanmak için vardır:

ostream& operator<< (ostream& (*pf)(ostream&)); 

lambda muhtemelen her şey utili tamamlıyor Bir yapıyı zing ve operator() tanımlamak, bu durumda tam olarak bir işlev işaretçisi gibi çalışacaktır. Bu benim için en olası açıklamadır, umarım yanlış olduğumda birisi beni düzeltebilir.

+1

Davranış yanlışlıkla değil, Standart tarafından zorunlu kılınmış. Hiçbir şey yakalamayan bir lambda * her zaman * düz bir eski işlev işaretçisine örtülü olarak dönüştürülebilir. (Ve tersine: bir şey * bir şey yakalama * bir lambda * asla * bir fonksiyon göstergesine dolaylı olarak dönüştürülebilir.) – Quuxplusone

5

Bu hatanın nedeni değildir, ancak dönüş türü ostream& sahip olarak lambdaManip ve functionManip hem tanımladığınız beri hem kenti hem de return stream; eklemek unuttuysanız inanıyoruz. Tanımlanmış operator<<(ostream&, std::function<ostream&(ostream&)> olduğundan


çağrı cout << functionManip başarısız olur. Bir tane ekle ve arama başarılı olacak.

ostream& operator<<(ostream& stream, function<ostream& (ostream&)>& func) { 
    return func(stream); 
} 

Alternatif olarak, bu operator<< tanımı eklemeden çalışacak functionManip

functionManip(cout); 

olarak çağırabilir. getAdditionFunctor tarafından döndürülen lambda bir yakalama-az lambda olduğundan, bir lambda dönen ilgili sorunuza gelince

, örtülü olarak bir işlev işaretçisi dönüştürülebilir.

typedef int(*addition_ptr)(int,int); 
addition_ptr getAdditionFunctor() 
{ 
    return [] (int x, int y) -> int { return x + y; }; 
} 

auto adder = getAdditionFunctor(); 
adder(10,20); // Outputs 30 
+0

İyi bir nokta, oyuncak örneğim için bunun hakkında düşünmeliydim. Ama ya yakalarım? Değişken yakalama, bir lambda'nın bir işlevden döndürülme noktasıdır; Eğer değişken yakalama yapmak istemediysem, ilk etapta sadece eski bir işlevi kullanırdım. –

+0

+1. Yakalamayan lambdaların işlev göstericilerine dolaylı olarak dönüştürülebilir olduğunu bilmiyordum, ama mükemmel bir anlam ifade ediyor. – Yuushi

3

önceki cevaplar sanat hakkının devlet var, ama sen std::function s ve/veya kodunuzda akışı manipülatörler olarak lambdas kullanılarak ağır olacak, o zaman sadece eklemek isteyebilirsiniz küresel kapsam, senin kod tabanına işlev şablon tanımını şu:

template<class M> 
auto operator<< (std::ostream& os, const M& m) -> decltype(m(os)) 
{ 
    return m(os); 
} 

orada expression SFINAE kullanır, bu nedenle m(os) iyi biçimlenmiş ifadesidir sürece bu özel operator<< aşırı yük bile aşırı yük çözünürlükte yer almayacak sondaki döndürme türü . (Bu, istediğiniz şeydir.)

Sonra da (

template<class T> 
auto commaize(const T& t) 
{ 
    return [&t](std::ostream& os) -> std::ostream& { 
     return os << t << ", "; 
    }; 
} 

int main() 
{ 
    std::cout << commaize("hello") << commaize("darling") << std::endl; 
} 

gibi şeyler yukarıdaki kodda herhangi std::function nesnelerin toplam eksikliği dikkat yapabilir! Sürece std::function nesnelerin içine doldurma lambdas kaçının kesinlikle gerekir, çünkü std::function nesnesini oluşturmak pahalı olabilir, hatta bellek ayırmayı da içerebilir.)

Standart kitaplığa bu operator<< aşırı yükünü eklemek C++ 17 için anlamlıdır, ancak farkında değilim o bölgedeki herhangi bir somut teklifin nt.

İlgili konular