2014-09-19 33 views
0

我想定義可以用於任意類型的自定義常量(例如floatdouble等)。作爲一個例子,假設我想定義一個常量,其值爲pi。沒有C++ 14的定義模板常量的正確方法?

明顯的解決方案是使用#define pi 3.14159265359,但然後pi不會在名稱空間中,我冒着名稱衝突的風險。我沒有使用C++ 14,所以我不能使用variable template。我能想到做到這一點,最好的辦法是這樣的:

#include <iostream> 

using namespace std; 

namespace constants { 
    template<typename T> T pi() { 
     return 3.14159265359; 
    } 
} 

int main() { 
    float pitest = 0; 
    pitest = constants::pi<float>(); 
    cout << pitest << endl; 
    cout << constants::pi<long double>() << endl; 
    cout << constants::pi<int>() << endl; 

    return 0; 
} 

我現在可以在命名空間中定義這些常量,我可以使用任意(數值)爲需要的類型。然而,這至少有兩個不希望的特徵:

  1. 它需要一個不應該是必要的函數調用(它只是一個常量!)。
  2. 即使函數正在返回已知類型的變量,我也必須在函數調用中指定類型。例如,在上面的代碼中,我必須使用pitest = constants::pi<float>();而不是簡單的pitest = constants::pi();,即使pitest顯然是float

有沒有更好的方法來做到這一點?

回答

6

爲什麼不使用自動轉換爲任何類型的特殊對象?

static struct { 
    template<class T> operator T() const constexpr 
    { return (T)3.14159265359; } 
} pi; 

你甚至可以爲更大的類型,任意精度算術,公式系統等等添加特化。

2
static struct { template<class T> operator T() const constexpr { return 3.14; } } pi; 

是第一步。

template<class T> struct type {}; 
template<class T> constexpr T get_pi(type<T>) { return 3.14; } 
static struct { template<class T> operator T() const constexpr { return get_pi(type<T>{}); } } pi; 

是第二種類型 - 現在您可以爲新類型添加新的重載,而無需專門化。所有pi確實是做魔術鑄造。

可悲的是,這要求我們完全匹配的類型 - 爲int新的過載不會解決long,或double新的過載不會解決float

但這是C++,我們可以做到!

template<class T> struct contra_type { 
    constexpr contra_type(contra_type&&) {}; 

    template<class U, class=typename std::enable_if< std::is_convertible< T, U >::value >::type> 
    constexpr contra_type(contra_type<U>&&) {} 
}; 
template<class T> constexpr auto get_pi(type<T>, ...)->decltype(T(3.14)) { return T(3.14); } 
static struct { template<class T> operator T() const constexpr { return get_pi(contra_type<T>{}); } } pi; 

是下一步。現在我們可以爲get_pi(type<bignum>)等添加重載並使其工作。實際上,任何可以從bignum隱式轉換的內容都會自動調用get_pi(type<bignum>)

不知道如何啓用ADL - 如果我採取T*,我會得到協變重載而不是逆變重載(並且因爲我們實際上在返回類型上重載,這不是我想要的)。

contra_type<U>可轉換爲contra_type<T>當且僅當T可轉換爲U。這意味着pi_func(contra_type<Foo>{})將嘗試找到pi_func,該類型可以轉換爲Foo,然後調用該類型。

...重載爲我們提供了一個完全匹配所有內容的默認實現,但因爲它有...,所以最好調用任何其他函數而不是匹配它。

+0

對於我打算使用這些常量而言,這有點矯枉過正,但對於超出範圍而言是+1。 – Null 2014-09-19 19:46:55

+1

@Null沒有像過度工程這樣的殺戮。 – Yakk 2014-09-19 19:55:18