2016-06-28 71 views
7

我給下面的代碼,以顯示我的問題:如何知道C++模板是容器還是類型?

template<T> 
void my_fun(T &obj) 
{ 
    if(obj is a type like float, std::string, double) 
    { 
     perform1() 
    } 
    if(obj is a container like std::vector, std::list) 
    { 
     perform2() 
} 
} 
std::vector<int> abc; 
my_fun(abc); 
int d; 
my_fun(d); 

然後我的問題,我怎麼能知道模板是指簡單類型或容器?謝謝。

+0

除了看起來像一個糟糕的設計,我建議你看一下在設計相反,你可以使用[ *型性狀*](http://en.cppreference.com/w/cpp/types#Type_traits_.28since_C.2B.2B11.29)。您可能需要實現適合您的特定類型特徵,例如'is_container'。 –

+1

'std :: string'當然是一個字符的容器,所以這個問題假設了一個錯誤的二分法。 – MSalters

回答

2

您在您的處置幾個選項。

  • 如果你想有一個默認的行爲,並改變它只有一種類型(或幾個),使用模板特您的功能。

例如:

template<typename T> 
void myfunc() { /*default*/ } 


template<> 
void myfunc<int>() { /*specialized version for int */} 
  • 如果你想改變你的函數行爲的類型一般基團,可以使用Type Traits(像std::is_fundamental)。你可能必須在這種情況下實現你自己的類型特徵。
+0

如果'std :: begin(std :: declval ())'有效,那麼這種特徵的一種可能的實現方法是SFINAE檢查。 – HolyBlackCat

1

A(參數化)容器一個類型。你可以,但是,過載它:

#include <iostream> 
#include <vector> 

template<typename T> 
void my_fun(T &obj) 
{ 
    perform1(); 
} 

template<typename T> 
void my_fun(std::vector<T> &obj) 
{ 
    perform2(); 
} 

int main(void) 
{ 
    int    a; 
    std::vector<int> b; 

    my_fun(a); 
    my_fun(b); 
} 

如果這還不夠,您還可以使用std::enable_if<>,這樣你就不需要寫的那部分的兩倍。

+1

你不能部分專門化一個功能。你在代碼中做的是用不同的模板重載函數。 – Holt

+0

這不是你如何專注模板。 – rustyx

+0

如果您要在答案中提供代碼,請提供實際編譯的正確語法的代碼。 –

3

你可以通過expression SFINAE寫上你自己的特質和enable_if多重過載。這裏是使用void_ttrick(將推測出現在C++ 17)的溶液中:

#include <iostream> 
#include <type_traits> 
#include <vector> 

template<typename ...> 
using to_void = void; // maps everything to void, used in non-evaluated contexts 

template<typename T, typename = void> 
struct is_container : std::false_type 
{}; 

template<typename T> 
struct is_container<T, 
     to_void<decltype(std::declval<T>().begin()), 
       decltype(std::declval<T>().end()), 
       typename T::value_type 
     >> : std::true_type // will be enabled for iterable objects 
{}; 

template<typename T> 
void f(T param, typename std::enable_if<is_container<T>::value>::type* = nullptr) 
{ 
    std::cout << "Container\n"; 
} 

template<typename T> 
void f(T param, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr) 
{ 
    std::cout << "Fundamental\n"; 
} 

template<typename T> 
void f(T param, 
    typename std::enable_if<!std::is_fundamental<T>::value>::type* = nullptr, 
    typename std::enable_if<!is_container<T>::value>::type* = nullptr) 
{ 
    std::cout << "Other\n"; 
} 

struct Foo{}; 

int main() 
{ 
    int x{};   // fundamental 
    std::vector<int> v; // container 
    Foo s{}; // other 

    f(x); 
    f(v); 
    f(s); 
} 

Live on Coliru

1

my_fun可以如使用SFINAE以下來實現。

namespace details{ 
    struct A{}; 
    struct B:A{}; 

    // A container will have a begin and an end. Also make it first prerference 
    template<typename T> 
    auto my_fun_impl(T const & obj, B *) -> decltype(obj.begin(),obj.end(),void()) 
    { 
     std::cout<<"Container\n"; 
    } 

    // Default choice 
    template<typename T> 
    auto my_fun_impl(T const & obj,A*) -> void 
    { 
     std::cout<<"Other than Container\n"; 
    } 
} 
template<typename T> 
auto my_fun(T const & obj) -> void 
{ 
    details::my_fun_impl(obj,static_cast<details::B *>(0)); 
} 

注意這裏傳遞一個BaseDerived類指針,否則編譯器會抱怨不明確的功能定義。

編譯器將嘗試匹配my_fun_impl的確切簽名與B pointer,它將在容器的情況下成功。因爲一個容器將有begin()和end(),預計在尾隨返回類型中。

在非容器類型的情況下,第一選項將不匹配。而且我們知道一個Base類指針可以容納派生類對象,所以默認匹配會成功。

及後續測試代碼的輸出

int main() 
{ 
    my_fun(std::vector<int>{1,2,3}); 
    my_fun(1); 
} 

Container 
Other than Container 

Demo on coliru

+0

[供參考]並非所有的容器都提供開始和結束成員功能。 'std :: queue'來自我頭頂。 – NathanOliver

+0

@NathanOliver,FWIW,'std :: queue'不是一個容器,而是一個容器適配器。 – chris

+0

@chris啊是的。然後我認爲所有的實際容器都有開始和結束。 – NathanOliver

相關問題