2015-02-24 102 views
11

所以我有這樣的真醜代碼:是否有這樣的事情了模板化的case語句

template <typename T> 
std::conditional_t<sizeof(T) == sizeof(char), 
        char, 
        conditional_t<sizeof(T) == sizeof(short), 
           short, 
           conditional_t<sizeof(T) == sizeof(long), 
               long, 
               enable_if_t<sizeof(T) == sizeof(long long), 
                  long long>>>> foo(T bar){return reinterpret_cast<decltype(foo(bar))>(bar);} 

我使用嵌套conditional_t s到做各種各樣的case語句。有沒有什麼能夠更加優雅地完成這件事,還是我需要製作自己的模板化案例陳述?

注:其實我知道,這使用的reinterpret_cast不好:Why Doesn't reinterpret_cast Force copy_n for Casts between Same-Sized Types?

+1

這段代碼試圖實現什麼? – Slava 2015-02-24 15:11:43

+0

你可以爲char,short,int,long long做一個模板... – amchacon 2015-02-24 15:11:54

+0

爲什麼這感覺就像你的依賴類型編程語言之後? – 2015-02-24 15:12:20

回答

10

我不得不這樣做一次,所以我寫了一個small wrapper來實現結果的整齊。你可以使用它,如下所示(請參閱​​用於測試)

template<class T> 
typename static_switch<sizeof(T) 
      ,int // default case 
      ,static_case<sizeof(char),char> 
      ,static_case<sizeof(short),short> 
      ,static_case<sizeof(long),long> 
      >::type foo(T bar){ ... } 

它幾乎做什麼你已經在幕後,但通過包裝它,我們把它(更多)可讀。如果你需要的話,還有一個版本可以讓你在類型T上切換direclty。

編輯:在@這裏Deduplicator的建議是它背後

#include <type_traits> 

/* 
* Select a type based on the value of a compile-time constant such as a 
* constexpr or #define using static_switch. 
*/ 

template<int I,class T> 
struct static_case { 
    static constexpr int value = I; 
    using type = T; 
}; 

template<int I, class DefaultType, class Case1, class... OtherCases> 
struct static_switch{ 
    using type = typename std::conditional< I==Case1::value , 
        typename Case1::type, 
        typename static_switch<I,DefaultType,OtherCases...>::type 
        >::type; 
}; 

struct fail_on_default {}; 

template<int I, class DefaultType, class LastCase> 
struct static_switch<I,DefaultType,LastCase> { 
    using type = typename std::conditional< I==LastCase::value , 
        typename LastCase::type, 
        DefaultType 
        >::type; 

    static_assert(!(std::is_same<type, fail_on_default>::value), 
        "Default case reached in static_switch!"); 
}; 
+3

你可能想在這裏從github導入你的代碼......這將使答案完整。無論如何,我只是在寫這樣的東西,但你明白了。 – Deduplicator 2015-02-24 15:25:16

+0

@丹我不知道我懂得如何應用這個。如果你可以插入它,測試應該相當簡單:'auto val = foo(13.0); static_assert(is_same :: value);' – 2015-02-24 15:25:19

+0

@JonathanMee:另外,如果值可以從類型派生,那麼它可以被簡化,就像你的情況一樣。 – Deduplicator 2015-02-24 15:29:38

2

像這樣的東西可能:

template <size_t N> struct SuitablySized; 

template<> struct SuitablySized<sizeof(char)> { 
    typedef char type; 
}; 
template<> struct SuitablySized<sizeof(short)> { 
    typedef short type; 
}; 
// Add more cases to taste 

template <typename T> 
typename SuitablySized<sizeof(T)>::type foo(T bar); 
+2

我們希望OP預先清除所有相同大小的類型... – Deduplicator 2015-02-24 15:22:37

4

switch語句的模板版本是一個專門的模板。

template<size_t n> struct matching_type; 
template<> struct matching_type<sizeof(char)> { typedef char type; }; 
template<> struct matching_type<sizeof(short)> { typedef short type; }; 
template<> struct matching_type<sizeof(int)> { typedef int type; }; 
template<> struct matching_type<sizeof(long)> { typedef long type; }; 
template<> struct matching_type<sizeof(long long)> { typedef long long type; }; 

template<typename T> 
matching_type<sizeof(T)>::type foo(T bar) 
{ 
    return reinterpret_cast<decltype(foo(bar))>(bar); 
} 
+4

希望OP預先清除所有相同大小的類型... – Deduplicator 2015-02-24 15:23:07

+0

請注意,在某些系統中,sizeof(int)可能等於sizeof( long)',這段代碼不會被編譯 – borisbn 2015-02-24 15:51:33

+2

是的,但'switch'語句有同樣的問題! – 2015-02-24 16:27:51

3

只要你理解了風險的代碼,同樣大小的類型可能不是敞篷車,你可以簡單地插件一個mpl::map ..

typedef map< 
     pair<int_<sizeof(char)>, char>, 
     pair<int_<sizeof(short)>, short>, 
     pair<int_<sizeof(int)>, int>, 
     pair<int_<sizeof(long long)>, long long> 
    > m; 

eg

#include <algorithm> 
#include <iostream> 

#include <boost/mpl/at.hpp> 
#include <boost/mpl/map.hpp> 

using namespace boost::mpl; 

typedef map< 
     pair<int_<sizeof(char)>, char>, 
     pair<int_<sizeof(short)>, short>, 
     pair<int_<sizeof(int)>, int>, 
     pair<int_<sizeof(long long)>, long long> 
    > m; 

template <typename T> 
typename at<m, int_<sizeof(T)>>::type foo(T bar) 
{ return reinterpret_cast<decltype(foo(bar))>(bar); } 


struct doh 
{ 
    std::string a, b, c; 
}; 

int main() 
{ 
    { 
     char c; 
     static_assert(std::is_same<decltype(foo(c)), char>::value, "error"); 
    } 
    { 
     short c; 
     static_assert(std::is_same<decltype(foo(c)), short>::value, "error"); 
    } 
    { 
     int c; 
     static_assert(std::is_same<decltype(foo(c)), int>::value, "error"); 
    } 
    { 
     long long c; 
     static_assert(std::is_same<decltype(foo(c)), long long>::value, "error"); 
    } 
    { 
     double c; 
     static_assert(std::is_same<decltype(foo(c)), long long>::value, "error"); 
    }  
    { 
     doh c; 
     static_assert(std::is_same<decltype(foo(c)), void_>::value, "error"); 
    }  
} 
+0

這看起來像一個有前途的解決方案。但我沒有提升。我想知道這是否可以通過'std :: map'實現? – 2015-02-24 16:28:03

+1

@JonathanMee,不可能用'std :: map',你可以用'switch'來完成,但它會是一個運行時測試而不是編譯時間... – Nim 2015-02-25 08:09:15

1

A型標籤:

template<class T>struct tag{using type=T;}; 

void_t(即將在C++ 17的編譯器,你附近):

template<class...>struct voider{using type=void;}; 
template<class...Ts>using void_t=typename voider<Ts...>::type; 

enable_first_t需要的std::enable_if一包(注意缺乏_t),並返回通過測試的第一個。您可以使用tag<X>更換std::enable_if<true, X>

template<class T,class=void>struct has_type:std::false_type{}; 
template<class T>struct has_type<T, void_t<typename T::type>>:std::true_type{}; 

namespace details { 
    template<class, class...Ts> 
    struct enable_first {}; 
    template<class T0, class...Ts> 
    struct enable_first<std::enable_if_t< !has_type<T0>{} >, T0, Ts... >:enable_first<void, Ts...> {}; 
    template<class T0, class...Ts> 
    struct enable_first<std::enable_if_t< has_type<T0>{} >, T0, Ts...>:T0 {}; 
} 

template<class...Ts>using enable_first_t=typename details::enable_first<void, Ts...>::type; 

template<class T> 
using result = enable_first_t< 
    std::enable_if<sizeof(T)==sizeof(char), char>, 
    std::enable_if<sizeof(T)==sizeof(short), short>, 
    std::enable_if<sizeof(T)==sizeof(long), long>, 
    tag<int> // default 
>; 

這種行爲很像0​​,但語句是完全布爾表達式。

live example

+0

爲什麼不使用'template void_t =無效;'? – dyp 2015-02-24 23:20:53

相關問題