2016-03-23 24 views
2

Farz edelim ki, bazı türler ve kullanıcıların bu türlere örnek oluşturmasını beklediğim bir şablon işlevi sağlayan bir kitaplık tasarlıyorum. Şablon işlevinin imza şablon parametresinin içine yuvalanmış typedefs bağlıdır ve ben şablon uyumsuz bir türle örneği ise güzel bir hata mesajı vermek static_assert kullanmak istiyorum:İşlev imzası geçersiz olduğunda bile static_assert tetikleme

// library.h 
#include <type_traits> 

struct compatible_with_f {}; 

struct foo : compatible_with_f { 
    using some_type = int; 
}; 

struct bar : compatible_with_f { 
    using some_type = float; 
}; 

template <typename T> 
void f(typename T::some_type param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    // some code, which also uses the type T itself directly 
} 

// main.cc 

int main() { 
    f<foo>(0); // works 
    f<bar>(0.f); // works 
    f<int>(0); // error: no matching function for call to 'f' 
} 

Bu gibi çalışmıyor Umarım: Dostu hata mesajım basılmaz, çünkü f imzası mantıklı değildir, bu nedenle derleyici, fonksiyonun gövdesinde bile static_assert'a ulaşmadan kurtarır.

İyi bir şekilde istediğimi elde etmenin bir yolu var mı?

// library.h 
#include <type_traits> 

struct compatible_with_f {}; 

struct foo : compatible_with_f { 
    using some_type = int; 
}; 

struct anything { 
    template <typename T> 
    anything(T&&) {} 
}; 

template <typename, typename = void> 
struct get_some_type { 
    using type = anything; 
}; 

template <typename T> 
struct get_some_type<T, typename std::enable_if<std::is_base_of<compatible_with_f, T>::value>::type> { 
    using type = typename T::some_type; 
}; 

template <typename T> 
void f(typename get_some_type<T>::type param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    // some code, which also uses the type T itself directly 
} 

// main.cc 

int main() { 
    f<foo>(0); // works 
    f<int>(0); // error: the template parameter to f should be `foo` or `bar` 
} 

Ben SFINAE şeyden dönüştürülebilen bir varsayılan some_type sağlamak için kullandığımız: İşte ile gelebilir en iyisidir. İmza her zaman anlamlıdır, bu yüzden derleyici fonksiyonun gövdesini anlar ve güzel hata mesajını alırım. Ama ben bu çözümü sevmiyorum: Bu aşırı karmaşık görünüyor ve f imzası artık çok daha az açık olan büyük bir dezavantajı var - T::some_type aldı, şimdi açık, şimdi bazı tuhaf özellik sihir alır. Daha iyi bir yolu var mı?

Sen gerçek uygulama için geçirmeden önce kontrol yapmak için bir yönlendirme fonksiyonunu yazabiliriz

cevap

0

:

template <typename T> 
void f_impl(typename T::some_type param) { 
    // some code, which also uses the type T itself directly 
} 

template<typename U, typename T> 
void f(T&& t) 
{ 
    static_assert(std::is_base_of<compatible_with_f, U>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    f_impl<U>(std::forward<T>(t)); 
} 

LIVE

DÜZENLEME: Alternatif

, sizin için daha az özelleşmiş aşırı ekleyebilirsiniz f:

template <typename T> 
void f(T param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
} 

template <typename T> 
void f(typename T::some_type param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    // some code, which also uses the type T itself directly 
} 

LIVE

+0

Önerilen çözümümün en büyük dezavantajı, kamuya açık API işlevinin daha az açık hale gelmesi imzasıydı. Bu, bundan daha da açık görünmüyor. – STU

+0

@STU Eh, en azından daha basit :) Ayrıca, imzayı açık tutabileceğinizi ve SFINAE ... – Rostislav

+0

@STU Oh bekleyişine bağlı olarak ayarlanan işlevden kaldırılmayı engelleyebileceğinizi sanmıyorum. Düzenlememe bak. – Rostislav

İlgili konular