2012-06-02 18 views
5

varsayalım Sonra bazı yan etkisi yapar ve bir fonksiyon bir cevap verir vardır: Ben bir işlev işaretçisi foo bağlamak istiyorumfonksiyon işaretçileri ve döndürme türü dönüşümleri

int foo() 
{ 
    perform_some_side_effect(); 
    return 42; 
} 

ama ilgilenmiyorum cevap, sadece yan etki:

error: invalid conversion from ‘int (*)()’ to ‘void (*)()’ 
:

void (*bar)() = foo; 

Ancak bu bir tür hata gibi görünen

Bu hatanın ardındaki mantık nedir? Tip sistemi neden cevabı göz ardı etmeme izin vermiyor? Ben de işlev işaretçisi sarın eğer Yan not


, çalışıyor bir std::function:

std::function<void()> baz = foo; 

Nasıl std::function (görünüşte) tipi sisteminde bu kısıtlamayı aşmak için yönetir?

+1

rasyonel farklı tipleri vardır sadece budur çalışması için bazı hile sürer. Bunu (rızanızla) 'reinterpret_cast' ile geçersiz kılabilirsiniz. –

+0

@StevenBurnap: Soru * neden * dönüşemiyoruz. Farklı türler oldukları açıktır. Bir float, 'int' değildir, ancak bunları dönüştürebilirsiniz. – Puppy

+0

Bunun sebebi, genel bir kural olarak, dolaylı olarak casting işaretçilerine izin verilmemesidir. –

cevap

9

What is the rationale behind that error? Why doesn't the type system allow me to ignore the answer?

nedeni türleri farklı olmasıdır, ve (fonksiyon: işaretçi aracılığıyla) çağrı yerde üretilen kod farklıdır. Tüm argümanların yığına yazıldığı ve dönüş değeri için alanın da yığınta saklandığı bir çağrı sözleşmesi düşünün. Arama bir void (*)() içinden inerse uzay dönüş değeri için yığınında ayrılacaktır, ama (o çağrılan nasıl habersiz) işlevi hala arayan alanı ayırdık gereken konuma 42 yazacak.

How does std::function (apparently) manage to circumvent this restriction in the type system?

Yapmıyor. Çağrıyı gerçek işleve saran bir işlev nesnesi oluşturur. derleyici foo aramayı işler Şimdi ne zaman o kadar çağrı kuralı uyarınca çağrısı bir int ve yapacak döndüren bir işlev için ne yapması gerektiğini bilir

void operator()() const { 
    foo(); 
} 

: Bu gibi bir üyesini içerecek . Şablon geri dönmediği için, değeri gerçekten dikkate almaz - aslında geri döndü.

+1

+1: En eksiksiz cevap. –

1

std::function sadece kaynak compatible-, bu sonuç yok sayar yeni caling kod üreten yeni bir sınıf oluşturabilir olması yeterlidir. Fonksiyon göstergesinin ikili uyumlu olması ve aynı işi yapamaması için void(*)() ve int(*)() noktalarının aynı kodu kullanamaması gerekir.

1

Sen özel durumda için yapıyorum std::function<> düşünebilirsiniz:

void __func_void() 
{ 
    foo(); 
} 

Aslında bundan biraz daha karışık ama nokta, değil tip silme ile birlikte şablon kodunu üretir olmasıdır özellikleri hakkında bakım.

1

diğerleri söyleyerek ifade ettiği anlamın yanı sıra, arayan da (geri dönüş değeri geçici olabilir) o sonuca çağırmak gerektiğini yıkıcı bilmek dönüş türü gerekir.


Maalesef öyle değil kadar kolay

auto (*bar)() = foo; 

GCC ve Clang bunu kabul olsa olarak. Aslında doğru olup olmadığını görmek için özellikleri tekrar kontrol etmem gerekiyor.

Güncelleme: Spec Bu hızlı okuma, ancak bu kadar GCC ve clang tarafından uygulanan edildiğinde yanıltıcı olabilir

The auto type-specifier signifies that the type of a variable being declared shall be deduced from its initializer or that a function declarator shall include a trailing-return-type.

sadece toplevel Bildiricisi için geçerli diyor. Bizim durumumuzda, bu bir işaretçi deklarasyonudur. İçerisinde bulunan declarator, bir işlev declarator. Yani sadece void için auto yerine ve derleyici sizin için türünü çıkartacaktır. Bu arada


, her zaman elle bu işi yapabilir, ama buna

template<typename FunctionType> 
struct Params; 

template<typename ...Params> 
struct Params<void(Params...)> { 
    template<typename T> 
    using Identity = T; 

    template<typename R> 
    static Identity<R(Params...)> *get(R f(Params...)) { 
    return f; 
    } 
}; 

// now it's easy 
auto bar = Params<void()>::get(foo); 
+0

Bu bile VS2011 RC'de derlenmiştir. otomatik çubuk = Test; \t bar(); – Jagannath