2010-04-09 24 views
15

İki derleyici arasında (g ++ 4.5, VS2010 RC) sınıf şablonlarının kısmi uzmanlıklarıyla lambdalarla eşleşmelerinde bazı tutarsızlıklar gözlemledim. Ben tür özellikleri çıkarmak için lambdas için boost :: function_types gibi bir şey uygulamak için çalışıyordum. Daha fazla ayrıntı için this kontrol edin.lambda özellikleri tutarsızlığı

g ++ 4.5 sürümünde, bir lambda'nın operator() tipi serbest duran fonksiyonunki gibi (R (*) (...)), VS2010 RC'de ise, üye işlevi (R (C :: *) (...)). Yani soru derleyici yazarlar istedikleri şekilde yorumlamakta özgürler? Değilse hangi derleyici doğrudur? Aşağıdaki ayrıntılara bakın.

template <typename T> 
struct function_traits 
    : function_traits<decltype(&T::operator())> 
{ 
// This generic template is instantiated on both the compilers as expected. 
}; 

template <typename R, typename C> 
struct function_traits<R (C::*)() const> { // inherits from this one on VS2010 RC 
    typedef R result_type; 
}; 

template <typename R> 
struct function_traits<R (*)()> { // inherits from this one on g++ 4.5 
    typedef R result_type; 
}; 

int main(void) { 
    auto lambda = []{}; 
    function_traits<decltype(lambda)>::result_type *r; // void * 
} 

Bu program, her iki g ++ 4.5 ve VS2010 üzerinde derler ancak kod belirtildiği gibi örneklenen function_traits farklıdır.

cevap

4

GCC'nin uyumlu olmadığını düşünüyorum. N3092 §5.1.2/5 der

bir lambda ekspresyonu için kapalı tip olan paramtereleri ve dönüş tip tarafından tarif edilen bir genel hat içi işlev çağrısı operatörü (13.5.4) sahip olmasıdır lambda-expression'ın parametre-bildirimi-ve sırasıyla geri dönüş-türü. Bu işlev çağrısı işleci, bildirilen const (9.3.1) ise ve yalnızca lambda ifadesinin parametresi bildirim-cümlesi değilse ve bunu değiştirilemiyorsa. kapatma nesnenin türü hakkında pek çok şey uygulama tanımlı ise

Yani, fonksiyon kendisi public olmak için üye olmalı ve const olmak statik olmayan üye olmalıdır.

DÜZENLEME: Bu program, operator()'un, GCC 4.6'daki bir üye işlevi olduğunu belirtir; bu, 4.5 ile aynıdır.

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

template< class ... > struct print_types {}; 

template<> struct print_types<> { 
friend ostream &operator<< (ostream &lhs, print_types const &rhs) { 
    return lhs; 
} 
}; 

template< class H, class ... T > struct print_types<H, T...> { 
friend ostream &operator<< (ostream &lhs, print_types const &rhs) { 
    lhs << typeid(H).name() << " " << print_types<T...>(); 
    return lhs; 
} 
}; 

template< class T > 
struct spectfun { 
friend ostream &operator<< (ostream &lhs, spectfun const &rhs) { 
    lhs << "unknown"; 
    return lhs; 
} 
}; 

template< class R, class ... A > 
struct spectfun< R (*)(A ...) > { 
friend ostream &operator<< (ostream &lhs, spectfun const &rhs) { 
    lhs << "returns " << print_types<R>() 
    << " takes " << print_types<A ...>(); 
    return lhs; 
} 
}; 

template< class C, class R, class ... A > 
struct spectfun< R (C::*)(A ...) > { 
friend ostream &operator<< (ostream &lhs, spectfun const &rhs) { 
    lhs << "member of " << print_types<C>() << ", " << spectfun<R (*)(A...)>(); 
    return lhs; 
} 
}; 

template< class T > 
struct getcall { 
typedef decltype(&T::operator()) type; 
}; 

int main() { 
int counter = 0; 

auto count = [=](int) mutable { return ++ counter; }; 

cerr << spectfun< getcall<decltype(count)>::type >() << endl; 
} 

çıkışı:

member of Z4mainEUlvE_, returns i takes i 

DÜZENLEME: Tek sorun belli kapatma çağrısı operatörlerine işaretçileri ptmf şablon kalıplarını maç için başarısız olmasıdır gibi görünüyor. Çözüm, lambda mutable ifadesini bildirmektir. Yakalama olmadığında ve sadece (sorunun giderilmesinin dışında) arama operatörünün yapısını değiştirirse, bu anlamsızdır.

template< class T > 
struct getcall { 
    typedef decltype(&T::operator()) type; 
    static type const value; 
}; 
template< class T > 
typename getcall<T>::type const getcall<T>::value = &T::operator(); 

int main() { 
    auto id = [](int x) mutable { return x; }; 
    int (*idp)(int) = id; 
    typedef decltype(id) idt; 
    int (idt::*idptmf)(int) /* const */ = getcall< decltype(id) >::value; 

cerr << spectfun< decltype(idp) >() << endl; 
cerr << spectfun< decltype(idptmf) >() << endl; 
cerr << spectfun< getcall<decltype(id)>::type >() << endl; 

çıkışı: değişken olmadan ve const ile

returns i takes i 
member of Z4mainEUliE0_ , returns i takes i 
member of Z4mainEUliE0_ , returns i takes i 

, spectfun son iki sorguları biri için imzalar yazdırmaz.

+1

Bu, ücretsiz işlevler kullandığından g ++ uyumlu değil miydi? – GManNickG

+0

Evet, bana yanlış anlatan GCC gibi geliyor. – jalf

+0

@Gman, jalf: Bah, seçici işitme. GCC'nin yöntemi daha fazla arkadaşlık gerektirir… ha! – Potatoswatter

1

n3043. Lambdalar artık herhangi bir eyaleti olmayan fonksiyon göstergelerine dönüştürülebilir. İnanıyorum ki (… ama bilmiyorum) GCC başlangıçta bu davranışı yanlışlıkla uyguladı, “düzelt”, şimdi tekrar 4.5 ya da 4.6'ya ekleyecek.VC10, başlangıçta tasarlandığı gibi lambdaları doğru şekilde uyguladı, ancak n3043 ile en son çalışma kağıtlarına uymuyordu.

+0

n3092 son taslağıdır: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf [PDF link] – GManNickG

+0

Evet, öyle - ama neden bahsediyorum n3043 özellikle. –

+0

Durum bilgisi olmayan lambdalar işlev göstericilerine dönüştürülebilir olsa da, daha iyi bir R (C :: *) (...) eşleşmesi olduğunda seçilebilir, değil mi? – Sumant

0

Bence gcc geliştiricilerin bu davranış için iyi bir nedeni var. Unutmayın, statik bir fonksiyonun "bu" işaretçisi yoktur ve aslında çağrıldığında, arayanın "bu" işaretçisini geçmesi gerekmez. Yani bu aslında kapatma nesnesinde bulunan hiçbir şey olmadığında küçük bir performans optimizasyonu. Ve G + + geliştiricisinin, lambda ifadesini "mutable" olarak bildirerek geçici bir çözüm yolunu bıraktığını görebilirsiniz (aslında mutasyona uğrayacak bir şeyiniz olmadığını unutmayın).