5

Kendimi C++ ile yeniden tanıştırmak ve C++ 0x'deki bazı heyecan verici özellikleri öğrenmek isteyen bir Scala/Java programcısıyım. Scala'nın koleksiyonlarına dayanan, kendi işlevsel kütüphanelerimi tasarlayarak başlamak istedim, böylece şablonların sağlam bir şekilde anlaşılmasını sağlayabileyim. Çalıştığım sorun, derleyicinin şablonlanmış işlev nesneleri için herhangi bir tür bilgisini çıkaramadığı görünüyor. "İmza" kullanarak bu sorunu çözdüğü anlaşılmaktadır. Bunlar result_type typename ile gerçekten benzer görünmektedir ve bunu yeni işlev sözdizimini kullanarak alacağımı düşündüm. Bu tür bir şeyi C++ 0x'da yapmanın bir yolu önerilebilir mi, yoksa mümkün mü yoksa en azından FC++ bunu nasıl başardı? İşteC++ 0x şablon işlev nesnesi çıkarsama

#include <vector> 
#include <iostream> 
#include <algorithm> 
using namespace std; 

template<class T> 
class ArrayBuffer { 
private: 
    vector<T> array; 
public: 
    ArrayBuffer(); 
    ArrayBuffer(vector<T> a) : array(a) {} 

    template<typename Fn> 
    void foreach(Fn fn) { 
     for(unsigned int i = 0; i < array.size(); i++) fn(array[i]); 
    } 

    template<typename Fn> 
    auto map(Fn fn) -> ArrayBuffer<decltype(fn(T()))> { 
     vector<decltype(fn(T()))> result(array.size()); 
     for(int unsigned i = 0; i < array.size(); i++) result[i] = fn(array[i]); 
     return result; 
    } 
}; 

template<typename T> 
class Print { 
    public: 
    void operator()(T elem) { cout<<elem<<endl; } 
}; 

template<typename T> 
class Square{ 
public: 
    auto operator()(T elem) -> T { 
     return elem * elem; 
    } 
}; 

int main() { 
    vector<int> some_list = {5, 3, 1, 2, 4}; 
    ArrayBuffer<int> iterable(some_list); 
    ArrayBuffer<int> squared = iterable.map(Square<int>()); // works as expected 
    iterable.foreach(Print<int>()); // Prints 25 9 1 4 16 as expected 
    iterable.foreach(Print()); // Is there a way or syntax for the compiler to infer that the template must be an int? 
    ArrayBuffer<int> squared2 = iterable.map(Square()); // Same as above - compiler should be able to infer the template. 
} 

cevap

5

oynamalar yaparak kod snippet'idir Sen de

class Print { 
    public: 
    template<typename T> 
    void operator()(T elem) { cout<<elem<<endl; } 
}; 

Sonra Print() geçebilir operator() bir şablon yapabilirsiniz bulunuyor. ArrayBuffer<decltype(fn(T()))> gibi argümanları geçen ben declval kullanmanızı öneririz, bu nedenle de varsayılan olmayan constructible T

ArrayBuffer<decltype(fn(declval<T>()))> 
+0

Teşekkür ederiz! Bu mükemmel çalışıyor. Şablonu operatöre taşımayı düşünmedim. Dekolt ile ilgili ipucunu da takdir ediyorum. Kısa bir takip yapmam gerekiyor - bu şekilde şablonlanmış bir işaretçi kullanarak bunu yapmanın bir yolu var mı? Örneğin, bir şablon baskı fonksiyonumuz varsa, iterable.foreach (& print ); Cevabın hayır olduğuna inanıyorum. –

0

ile işe yarayabilir derleyici şablon bir int olması gerektiğini anlaması için bir yol ya da sözdizimi var mı ?

sorun bir int olmak gerekmez yoktur şablondur.
Bu, eşit olarak geçerlidir.

iterable.foreach(Print<float>()); 

Gerçekten soruyorsunuz.
Bir şablonun kullanılmakta olan içeriğe bağlı olarak farklı bir varsayılan değeri var mı?

Cevap hayır. Basit vakalar için teorik olarak yapabiliriz. Ancak köşe kutuları bunu mümkün kılar. İşlevin gerçek bağlamı, yalnızca örneklendiği satır değil, tam bir derleme birimidir.

1

C++ standart kitaplığını yeniden oluşturuyor gibi görünüyorsunuz. Senin konteynırlarından bahsetmiyorum.
C++ zaten oldukça işlevsel bir şekilde donatılmıştır.

Sanırım C++ standart kitaplığı hakkında birkaç önemli noktayı kaçırıyorsunuz.

  • O Jenerik birinci ve Object Oriented saniyedir.
    Bir algoritma genel bir şekilde uygulanabiliyorsa, bu sınıfa dahil edilmeyecektir.
    ArrayBuffer::foreach == std::for_each
    ArrayBuffer::map == std::transform
  • algoritması, tam konteyner yerine yineleyicileri çalışır. Bu genellikle yeni C++ programcıları tarafından kaçırılır çünkü hem Java hem de C# kavramı eksiktir. Yineleyiciler, yalnızca konteynerlerden daha etkileyici/esnektir. Yineleyiciler, arguably yoludur.Yani, Ranges yineleyicileri ifade etmek için çok daha fazla yoldur (bir aralık sadece eşleştirilen yineleyicilerdir).

C++ işlevinin kullanımına ilişkin bir örnek. Ayrıca C# yineleyicileri kullanmadığına da iyi bir örnektir. Her ne kadar çok güçlü olsalar da, C# 'nin hedef kitlesine olan ayrılıkları korkutucudur. Java, Nesne Yönelimli olmadıkları için yineleyicileri kullanmaz ve dil tasarımcıları başlangıçta bunun hakkında çok analdılar.

struct Print 
{ 
    template<typename T> 
    void operator()(const T& t) 
    { std::cout << t << std::endl; } 
}; 

struct Squared 
{ 
    template<typename T> 
    T operator()(const T& t) 
    { return t*t; } 
}; 

int main() 
{ 
    std::vector<int> vi; 
    std::foreach(vi.begin(), vi.end(), Print()); 
    std::foreach(vi.begin(), vi.end(), [](int i){ std::cout<<i<<std::endl; }); 

    std::vector<int> vi_squared; 

    std::transform(vi.begin(), vi.end(), std::back_inserter(vi_squared), Squared()); 
    // or 
    vi_squared.resize(vi.size()); 
    std::transform(vi.begin(), vi.end(), vi_squared.begin(), Squared()); 
} 
+0

İpuçları için teşekkürler. STL'nin bazı algoritmaları kaplardan nasıl güzel bir şekilde ayırdığını anlıyorum. Hala katlar, azalmalar, görünümler, dilimler, flattens, flatMaps vb. Gibi pek çok şeyi bırakır. Son olarak, C++ 'daki dönüşüm yıkıcıdır. Değişken koleksiyonlara ek olarak tamamen kalıcı koleksiyonları uygulamak istiyorum ve bu STL'nin ayrık tasarımı ile yapılamaz. Bir harita konteynerde bir işlem olmalı ve konteyner nasıl uygulanacağını seçebilmelidir. –

+0

Aslında onu geri alıyorum. Dönüşümü yıkıcı görünmüyor. Yine de, operasyonların kapların bir parçası olarak kullanılmasının mantıklı olduğunu düşünüyorum ve her bir konteynerin kendine özgü davranışlarını yerine getirmesini sağlıyorum. Algoritmalarım yinelemeyle karıştırılacağından, bu tasarımdan ne kadar kod yeniden kullanabileceğimi bilmiyorum. Ayrıca bir hiyerarşi altında uyguladığım farklı koleksiyonları birleştirmek için CRTP kullanmayı ümit ediyorum, böylece bir kütüphaneden bir uygulama yerine bir arayüz sunabiliyorum (şablon yöntemleri sanal olamaz). Aralıklar harika görünüyor - bağlantıyı takdir ediyorum. –

+0

“std :: transform” veya herhangi bir std algoritmasında kullanıldığında bir kapsayıcı davranışını hala uzmanlaştırabilirsiniz. Harita yöntemini alt sınıflandırma ve geçersiz kılmak yerine, konteyneriniz için 'std :: transform' yöntemiyle uzmanlaştın (teknik olarak kısmi uzmanlık). Genellikle, koleksiyonunuzun nasıl geçtiğini tanımlamak yeterlidir ve yineleyiciler bunu gerçekten iyi yapar (ve aralıkları/görünümleri daha iyi yapar). Bu bana doğru geliyor. Harita algoritmasının zamanın% 99'unu depolama semantiği hakkında umursamıyor. Ama gerçekten oraya girip, çılgın şeyler yapman gerekiyorsa, uzmanlaşabilirsin. –