使用if语句进行C ++模板实例化

时间:2018-04-27 08:48:43

标签: c++

我想写一个sort函数,它接受包含多个容器的可变参数:void sort(C &c, Rest&... rest);

template<typename T>
struct is_list{
    static constexpr bool value = false;
};

template<typename T>
struct is_list<std::list<T>>{
    static constexpr bool value = true;
};

template<typename T>
struct is_list<std::forward_list<T>>{
    static constexpr bool value = true;
};

template<typename C, typename...Rest>
void sort(C &c, Rest&... rest){
    if(is_list<C>::value == true) c.sort(); 
    else std::sort(c.begin(), c.end());
    sort(rest...);
}

//main file
std::vector<int> vec{1, 2, 3};
sort(vec);

两个错误:

  1. vector没有名为'sort'的成员if(is_list<C>::value == true) c.sort();
  2. 但逻辑上if语句不应该执行,因为我传递了一个向量而不是列表。

    1. 没有匹配函数来调用sort(rest...);
    2. 的'sort()'

      第二个错误很明显,但错误1的原因是什么。

      我通过

      解决了这个问题
      template<typename T>
      struct is_list{
          static constexpr bool value = false;
      };
      
      template<typename T>
      struct is_list<std::list<T>>{
          static constexpr bool value = true;
      };
      
      template<typename T>
      struct is_list<std::forward_list<T>>{
          static constexpr bool value = true;
      };
      
      template<typename C, typename std::enable_if<!is_list<C>::value>::type* = nullptr>
      void sort(C &c){
          sort(c.begin(), c.end());
      }
      
      template<typename C, typename std::enable_if<is_list<C>::value>::type* = nullptr>
      void sort(C &c){
          c.sort();
      }   
      
      template<typename C, typename...Rest>
      void sort(C &c, Rest&... rest){
          sort(c);
          sort(rest...);
      }
      

2 个答案:

答案 0 :(得分:3)

第一个错误来自于编译器实例化模板时,它编译整个函数,而不仅仅是满足条件的if分支。

这意味着当您将向量传递给sort时,它会以C实例化为std::vector,但之后会尝试编译(nb:not执行),c.sort()无论如何。在这种情况下,std::vector没有sort()成员。这就是您收到此错误的原因。因为编译器会尝试实例化整个模板(即用实际类型替换C并编译每一行)。

要修复它,您需要重新设计模板,以便实例化为您的类型完全编译的版本。 std::enable_if可能是一个潜在的解决方案:

template<typename C, typename std::enable_if<is_list<C>::value>::type >
void sort(C &c){
    c.sort();
}

template<typename C, typename std::enable_if<!is_list<C>::value>::type* = nullptr>
void sort(C &c){
    std::sort(c.begin(), c.end());
}

See it live on Coliru

答案 1 :(得分:1)

一旦你看到了你的代码,你的代码就会遇到另一个问题 你没有处理递归的终端案例 我还添加了完美转发

如果您使用的是c ++ 17,则可以使用constexpr(如果

template<typename C, typename...Rest>
void sort(C &c, Rest&& ... rest){
    if constexpr (is_list<C>::value == true) c.sort(); 
    else std::sort(c.begin(), c.end());
    if constexpr (sizeof...(rest) >= 1) // empty case
        sort(std::forward<Rest>(rest)...);
}

否则你可以使用模板专业化,重载甚至SFINEA 但我认为重载是c ++ 11最简单的方法

//handleing empty case for terminal recursion
void sort() {} 

//list case
template<typename T, typename...Rest>
void sort(std::list<T>& c, Rest&& ... rest){
    c.sort();
    sort(std::forward<Rest>(rest)...);

}

//forward list case
template<typename T, typename...Rest>
void sort(std::forward_list<T>& c, Rest&& ... rest){
    c.sort();
    sort(std::forward<Rest>(rest)...);

}

//other containers case
template<typename C, typename...Rest>
void sort(C&& c, Rest&& ... rest) {
    std::sort(c.begin(), c.end());
    sort(std::forward<Rest>(rest)...);
}