2017-01-31 48 views
12

Gerçek örneğim oldukça büyüktür, bu yüzden basitleştirilmiş bir kullanacağım. örneğinİşaretçiler, değerler ve akıllı işaretçiler için C++ şablonları birleştirme

struct Rectangle { 
    int width; 
    int height; 

    int computeArea() { 
    return width * height; 
    } 
} 

Ve bu tür tüketir başka bir tür,: Ben bir dikdörtgen için veri türü olduğunu varsayalım Şimdi

struct TwoRectangles { 
    Rectangle a; 
    Rectangle b; 
    int computeArea() { 
    // Ignore case where they overlap for the sake of argument! 
    return a.computeArea() + b.computeArea(); 
    } 
}; 

, ben kullanıcıları üzerinde mülkiyet kısıtlamaları koymak istemiyoruz

template<typename T> 
struct TwoRectangles { 
    T a; 
    T b; 
    int computeArea() { 
    // Ignore case where they overlap for the sake of argument! 
    return a.computeArea() + b.computeArea(); 
    } 
}; 

kullanımları:

, o yüzden bir şablon yapmak istiyorum 210

sorun arayan işaretçileri kullanmak isterse, fonksiyonun gövdesi farklı olmasıdır:

template<typename T> 
struct TwoRectangles { 
    T a; 
    T b; 
    int computeArea() { 
    assert(a && b); 
    return a->computeArea() + b->computeArea(); 
    } 
}; 

kod Maksimum sayı miktarı böylece benim şablon işlevi birleştirme en iyi yolu nedir

işaretçiler, değerler ve akıllı işaretçiler için yeniden kullanılır? Size bu ifadelerin ikisi de geçerli olan için bir türüne sahip ihtimali düşüktür

template<typename T> 
struct TwoRectangles { 
    T a; 
    T b; 

    int computeArea() { 
    return areaOf(a) + areaOf(b); 
    } 

private: 
    template <class U> 
    auto areaOf(U& v) -> decltype(v->computeArea()) { 
     return v->computeArea(); 
    } 

    template <class U> 
    auto areaOf(U& v) -> decltype(v.computeArea()) { 
     return v.computeArea(); 
    } 
}; 

:

+7

Uygulanamaz olanları dışlamak için SFINAE ile bir sürü aşırı yüklenme yazabilirsiniz. Ya da sadece ihtiyaç duyduğunuz şeylerle başa çıkmak için tasarımınızı değiştirebilir ve ihtiyacınız olana kadar ihtiyaç duyabileceğiniz şeyleri dışarıda bırakabilirsiniz. –

+4

Yalnızca '->' kullanın. İşaretçi olmayan değerleri özel işaretçi benzeri nesnelere sarın. –

+1

Standart kütüphaneyi yapabilir ve ona ek bir şablon türü verebilirdiniz. Bu, "T" yi nasıl çekeceğini ve "computeArea" öğesini nasıl çağırdığını "bilen bir özelliktir. Ya da aşırı mühendisliğe değil. :) – GManNickG

cevap

21

Bir, bunu TwoRectangles içindeki her şeyi enkapsüle yolu, bir şey gibi olurdu. Ancak her zaman ikinci bir argümanla areaOf()'a ek anlam ayrımı ekleyebilirsiniz. std::invoke():


başka yolu da, zaten ne olursa olsun bir fonksiyonu çağıran standart kütüphanede bir yolu var olduğu gerçeğini yararlanmak olacaktır. (Bu büyük bir değişiklik olduğu için), ben aslında bir süre önce benzer bir sorun vardı

template <class T, class = void> 
struct element_type { 
    using type = T; 
}; 

template <class T> 
struct element_type<T, void_t<typename std::pointer_traits<T>::element_type>> { 
    using type = typename std::pointer_traits<T>::element_type; 
}; 

template <class T> 
using element_type_t = typename element_type<T>::type; 

ve

template<typename T> 
struct TwoRectangles { 
    T a; 
    T b; 

    int computeArea() { 
    using U = element_type_t<T>; 
    return std::invoke(&U::computeArea, a) + 
     std::invoke(&U::computeArea, b); 
    } 
}; 
2

sonunda ben şimdi için bunu yapmak için tercih: Sadece altta yatan türünü bilmek gerekir ama doğru gibi görünen bir çözüm üretti.

Herhangi bir dolaylılık varsa, temel değere erişmek için yardımcı bir işlev oluşturmayı düşündüm. Kodda buna benzer, sizinkilere benzeyen bir örnekle.

#include <iostream> 
#include <string> 
#include <memory> 

namespace detail 
{ 
    //for some reason the call for int* is ambiguous in newer standard (C++14?) when the function takes no parameters. That's a dirty workaround but it works... 
    template <class T, class SFINAE = decltype(*std::declval<T>())> 
    constexpr bool is_indirection(bool) 
    { 
     return true; 
    } 
    template <class T> 
    constexpr bool is_indirection(...) 
    { 
     return false; 
    } 
} 
template <class T> 
constexpr bool is_indirection() 
{ 
    return detail::is_indirection<T>(true); 
} 

template <class T, bool ind = is_indirection<T>()> 
struct underlying_type 
{ 
    using type = T; 
}; 

template <class T> 
struct underlying_type<T, true> 
{ 
    using type = typename std::remove_reference<decltype(*(std::declval<T>()))>::type; 
}; 

template <class T> 
typename std::enable_if<is_indirection<T>(), typename std::add_lvalue_reference<typename underlying_type<T>::type>::type>::type underlying_value(T&& val) 
{ 
    return *std::forward<T>(val); 
} 

template <class T> 
typename std::enable_if<!is_indirection<T>(), T&>::type underlying_value(T& val) 
{ 
    return val; 
} 
template <class T> 
typename std::enable_if<!is_indirection<T>(), const T&>::type underlying_value(const T& val) 
{ 
    return val; 
} 


template <class T> 
class Storage 
{ 
public: 
    T val; 
    void print() 
    { 
     std::cout << underlying_value(val) << '\n'; 
    } 
}; 

template <class T> 
class StringStorage 
{ 
public: 
    T str; 
    void printSize() 
    { 
     std::cout << underlying_value(str).size() << '\n'; 
    } 
}; 

int main() 
{ 
    int* a = new int(213); 
    std::string str = "some string"; 
    std::shared_ptr<std::string> strPtr = std::make_shared<std::string>(str); 
    Storage<int> sVal{ 1 }; 
    Storage<int*> sPtr{ a }; 
    Storage<std::string> sStrVal{ str }; 
    Storage<std::shared_ptr<std::string>> sStrPtr{ strPtr }; 
    StringStorage<std::string> ssStrVal{ str }; 
    StringStorage<const std::shared_ptr<std::string>> ssStrPtr{ strPtr }; 

    sVal.print(); 
    sPtr.print(); 
    sStrVal.print(); 
    sStrPtr.print(); 
    ssStrVal.printSize(); 
    ssStrPtr.printSize(); 

    std::cout << is_indirection<int*>() << '\n'; 
    std::cout << is_indirection<int>() << '\n'; 
    std::cout << is_indirection<std::shared_ptr<int>>() << '\n'; 
    std::cout << is_indirection<std::string>() << '\n'; 
    std::cout << is_indirection<std::unique_ptr<std::string>>() << '\n'; 
} 
+1

bu yüzden aşağı notu mu? Neyi kaçırdığımı söyle, ben – Sopel

+1

öğrenmekten memnun olurum. Bunun için birkaç sorun var. 1) 'T *' ve 'T const *' aşırı yükleri gereksizdir. 2) 'T const &' aşırı yüklenme, 'T &' aşırı yükü ile gereksiz ve tehlikelidir çünkü eğer bir rengin değerini iletirseniz, dışarıdan çıkan bir referans alırsınız 3) Bu, akıllı göstericileri desteklemez. – Barry

+0

@Barry teşekkürler.Her ne kadar const ve aşırı yüklenmeden denedim, ancak o zaman const değişkenleri ile çalışmıyordu (belki de yanlış bir şey mi yapıyordum?), Ki bence çok önemli. Ve bunun tehlikeli olduğunu kabul etmeme rağmen, röleler geçebilir, bunun gibi semantikleri değiştirmeye dair bütün fikir tehlikeli. Onu bıraktım. Kodda değişiklikler yaptım, şimdi akıllı işaretçilerle başa çıkmalı. Daha iyi mümkün olmalı ama şu an fazla zamanım yok. – Sopel