2016-06-28 145 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

相关问题