我怎么知道C ++模板是容器还是类型?

时间:2016-06-28 13:11:07

标签: c++ templates stl

我提供以下代码来显示我的问题:

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);

然后我的问题,我怎么知道模板是指简单类型还是容器?谢谢。

4 个答案:

答案 0 :(得分:4)

您可以通过expression SFINAE编写自己的特征和enable_if多次重载。这是一个使用void_t trick的解决方案(可能会出现在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 :(得分:2)

您有几种选择。

  • 如果您想要默认行为,并且仅针对一种类型(或少数类型)进行更改,请为您的函数使用模板专业化

例如:

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


template<>
void myfunc<int>() { /*specialized version for int */}
  • 如果要更改通用类型组的函数行为,可以使用Type Traits(类似std::is_fundamental)。您可能必须在这种情况下实现自己的类型特征。

答案 2 :(得分:1)

(参数化)容器类型。但是,你可以超载它:

#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<>,这样您就不需要再写两次。

答案 3 :(得分: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