2016-08-20 18 views
5

Bir std::pair ve aynı zamanda çiftin türlerinden birinin değerini alan bir işlev şablonum var. Bu işlevi, bir ikili argümanı olarak std::map'dan bir giriş kullanarak aramak istiyorum.Şablon bağımsız değişkendeki const/const-const uyuşmazlığı nasıl giderilir

#include <map> 
#include <utility> 

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) { 
    // Imagine that this does something important... 
} 

int main() { 
    std::map<int, float> foo { { 0, 0.0 } }; 

    do_stuff(*foo.begin(), 0); 
} 

Bu haritanın girişinin türü std::pair<const int, float> çünkü derlenmeyecektir, böylece T1 tip kesinti çelişkili tipleri vardır: val argüman yoluyla pair argüman yoluyla const int ve int.

test.cc: In function ‘int main()’: 
test.cc:12:27: error: no matching function for call to ‘do_stuff(std::pair<const int, float>&, int)’ 
    do_stuff(*foo.begin(), 0); 
         ^
test.cc:5:6: note: candidate: template<class T1, class T2> void do_stuff(const std::pair<_T1, _T2>&, const T1&) 
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) { 
     ^~~~~~~~ 
test.cc:5:6: note: template argument deduction/substitution failed: 
test.cc:12:27: note: deduced conflicting types for parameter ‘const T1’ (‘const int’ and ‘int’) 
    do_stuff(*foo.begin(), 0); 
         ^

Bu çakışmayı çözmenin en iyi yolu nedir? İdeal olarak T1'un int olarak çıkarılmasını isterim, ancak uygulamak daha kolaysa const int olmasını isterim. eğer

void do_stuff(std::pair<T1, T2> const &pair, typename std::remove_const<T1>::type const &val) { 

ama daha uygun olanların hangi bilmiyorum ya:

ben val parametrenin türüne ya std::remove_const veya std::decay kullanarak hatayı gidermek olabileceğini saptadık Daha iyi olabilecek başka bir çözüm var.

+0

anlamayacağınızdan emin değilsiniz ... neden buna göre do_stuff() 'i tanımlamıyorsunuz; 'Typename T1, typename T2> void do_stuff (std :: çifti const & çifti, T1 const & val) 'demek istedim? – max66

+0

@ max66, aynı zamanda ilk tip * * const * olmayan çiftlerle de çalışmasını istiyorum. (Tüm parametre const referansı tarafından geçirildiği için, çiftin üyelerinin sağlamlığı ilgisizdir; ya da bunlardan herhangi biri için const yapısına izin verilmelidir.) – Wyzard

+0

Haritaya yapım yapmayı denediniz mi? Std :: map foo {{0, 0.0}}; ' – mtb

cevap

5

Tek çözüm, const anahtar sözcüğünü kullanmak yerine std::add_const kullanmaktır. Gerçekten şık değil

#include <map> 
#include <type_traits> 
#include <utility> 

template< class T1, class T2 > 
void do_stuff(std::pair<T1, T2> const& pair, std::add_const_t<T1>& val) 
{ 
    // Imagine that this does something important... 
    (void) pair; (void) val; 
} 

auto main() 
    -> int 
{ 
    std::map<int, float> foo { { 0, 0.0f } }; 
    do_stuff(*foo.begin(), 0); 
} 
+4

'' auto main() -> int' anlamına gelmekteyim. Bunu çok ileri sürdünüz. – Dani

+1

@Dani' int' demek istiyor musunuz? onun kodundaki işlev adından çok uzak bir yeni satır mı? –

+0

@ Cheersandhth.-Alf Yani 'auto main' şimdi C++ 17'de izinli mi? (http://stackoverflow.com/questions/17134975/will-automatic-return-type-deduction-work-for-main) – kfsone

2

ama iki farklı şablon paramenters içinde T1 ayırmak herhalde:

bir şablon aracılığıyla gidiş dönüş bu parametre türü aracılığıyla tipi indirimi önler.

şey

gibi
template <typename T1, typename T2, typename T3> 
void do_stuff(std::pair<T1, T2> const &pair, T3 const &val) { 
    // Imagine that this does something important... 
} 
Sen T1 ve T3 arasında bir korelasyon empoze etmek, std::enable_if_t yoluyla, çek ekleyebilir

; örnek

template <typename T1, typename T2, typename T3, 
     typename = std::enable_if_t<std::is_same<std::remove_const_t<T1>, std::remove_const_t<T3>>::value>> 
void do_stuff(std::pair<T1, T2> const &pair, T3 const & val) { 
    // Imagine that this does something important... 
} 
+0

Bu derleme hatasını giderir, ancak arayanın tamamen farklı, ilişkisiz bir tür geçirmesine de izin verir. ("T3", "T1" ile uyuşmuyorsa, yine de bir derleme hatası oluşturur, ancak hata, sorunun gerçekte ne olabileceğinin net olma olasılığının düşük olduğu, uygulamanın gerçekleştirildiği yerdeki bir yerden gelir.) – Wyzard

+0

@Wyzard - SFINAE yoluyla, 'T1' ve 'T3' (daha az ya da çok) eşittir; öncekinden daha çirkin ... ama cevabımı buna göre değiştirdim. – max66

+0

'typename = typename std :: enable_if_t' ikinci 'typename 'gerekli değildir, bu' _t 'takma adının noktasıdır. – Oktalist

3
template<class T>struct identity{using type=T;}; 
template<class T>using no_deduce=typename identity<T>::type; 

sarın no_deduce ikinci tip kesinti engellemek için.

Bu ikisi de çalışıyor ve neden bunu yaptığınızı açık.

Şimdi, T1 bir başvuru türü ve const& bu durumda ne yaparsa yapmak istediğinizi dikkate almaya değer olabilir. T1'un int& olduğunu düşünün, daha sonra int& const& sadece bir int& olur.

İstediğiniz bu olmayabilir.

Belki istediğini, gerçekten gerçekten ne istediğini, geçerli:

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, std::remove_reference_t<T1> const &val) { 
    // Imagine that this does something important... 
} 

Eğer emin const olduğunu daha iyi hale modifiye edilmesini val önlemek istersen yapman gereken bir const&, senin & unutmak istiyorum . Şimdi, değerli const&, remove_reference_t'u boşa harcamayın ve sorun olmaz.

volatile ile anlaşma yapmak isterseniz, remove_volatile_t ile bağlantıya geçmeniz gerekir. Onları sonsuza dek template<class T>using clean_t=std::remove_cv_t<remove_reference_t<T>>'da bağla. Eğer const& istiyorsan sen Basitçe

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, clean_t<T1> const &val) { 
    // Imagine that this does something important... 
} 

const& kolay etmektir var, ama bu böyle.

İlgili konular