首页 > 解决方案 > “模拟”部分功能模板专业化?

问题描述

我知道部分模板专业化不适用于函数。但是有什么方法可以实现类似的东西吗?我的用例非常简单,但我不确定解决它的最佳方法是什么。

假设我正在编写一个接收 STL 容器的函数:

template<typename T>
void doSomething(T& container) {
    // do something here
}

std::forward_list现在,我想在传入和传入时对此函数有一个不同的定义std::list

起初我想也许我可以像这样重载函数:

// overload for std::list
template<typename T>
void insertionCppStl(std::list<T>& container) {
    // do seomthing here for list
}

但这意味着我也需要做同样的重载std::forward_list。这根本不理想。

有没有办法为两种不同的输入类型重载一个函数?寻找 C++17 的答案,但也欢迎 C++20 的答案。

标签: c++functiontemplatesc++17overloading

解决方案


不是一个很好的解决方案,但......

template <template <typename...> class C, typename T>
std::enable_if_t<std::is_same_v<C<T>, std::list<T>>
              || std::is_same_v<C<T>, std::forward_list<T>>>
    insertionCppStl (C<T> & container) {
    // do seomthing here for list
}

(注意:代码未经测试)

正如 Daniel Langr 所指出的(谢谢!),这仅适用于具有标准分配器的列表,因此 for std::list<T>and std::forward_list<T>,而不是 for std::list<T, A>and std::forward_list<T, A>where Ais an allocator from std::allocator<T>.

这对您来说是否足够,取决于您的需要。

更通用的解决方案也可以考虑分配器

template <template <typename...> class C, typename T, typename A>
std::enable_if_t<std::is_same_v<C<T, A>, std::list<T, A>>
              || std::is_same_v<C<T, A>, std::forward_list<T, A>>>
    insertionCppStl (C<T, A> & container) {
    // do seomthing here for list
}

显然这只适用于std::liststd::forward_list

正如 Barry 在评论中指出的那样(谢谢!),当您有两个(三个、四个......)具有共同点的类型(在本例中:具有兼容签名的模板类型)但不是一般解决方案。

不幸的是,我没有看到一个简单的通用解决方案......我建议开发一个特定的 type_traits 来为特定版本的函数选择可接受的类型。

例如:如果你想要一个特定版本的std::lists,std::forward_lists和std::arrays,你可以写如下内容

template <typename>
struct specific_foo : public std::false_type
 { };

template <typename T, typename A>
struct specific_foo<std::list<T, A>> : public std::true_type
 { };

template <typename T, typename A>
struct specific_foo<std::forward_list<T, A>> : public std::true_type
 { };

template <typename T, std::size_t N>
struct specific_foo<std::array<T, N>> : public std::true_type
 { };

然后是foo()函数的两个版本:特定版本(接收一个std::true_type作为第二个参数)和一个通用版本(接收一个std::false_type

template <typename T>
void foo (T const &, std::false_type)
 { std::cout << "generic version" << std::endl; }

template <typename T>
void foo (T const &, std::true_type)
 { std::cout << "specific version" << std::endl; }

现在您需要一个标签调度版本,foo()为第二个参数选择正确的类型

template <typename T>
void foo (T const & t)
 { foo(t, specific_foo<T>{}); }

下面是一个完整的编译示例

#include <list>
#include <array>
#include <vector>
#include <iostream>
#include <type_traits>
#include <forward_list>

template <typename>
struct specific_foo : public std::false_type
 { };

template <typename T, typename A>
struct specific_foo<std::list<T, A>> : public std::true_type
 { };

template <typename T, typename A>
struct specific_foo<std::forward_list<T, A>> : public std::true_type
 { };

template <typename T, std::size_t N>
struct specific_foo<std::array<T, N>> : public std::true_type
 { };

template <typename T>
void foo (T const &, std::false_type)
 { std::cout << "generic version" << std::endl; }

template <typename T>
void foo (T const &, std::true_type)
 { std::cout << "specific version" << std::endl; }

template <typename T>
void foo (T const & t)
 { foo(t, specific_foo<T>{}); }

int main ()
 {
   foo(0);                             // print "generic version"
   foo(std::list<int>{});              // print "specific version"
   foo(std::forward_list<long>{});     // print "specific version"
   foo(std::array<long long, 42u>{});  // print "specific version"
   foo(std::vector<char>{});           // print "generic version"
 }

推荐阅读