模板模板函数的重载 - 显式调用

overload of template template function - explicit call

因此,首先考虑以下从函数参数中隐式知道模板参数的情况:

#include <iostream>
using namespace std;

class A {};
class B {};

template <class T1, class T2>
class C { 
    T1 a; 
    T2 b; 
};

template <class T1>
class D {
    T1 a;
};

template <template<class, class> class TC, class TA, class TB>
void foo(TC<TA, TB> c) {
    std::cout << "T<T,T>" << std::endl;
};

template <template<class> class TD, class TA>
void foo(TD<TA> d){
    std::cout << "T<T>" << std::endl;
};

int main() {
    C<A,B> c;
    D<A> d;

    foo(c);
    foo(d);
}

输出如您所料:

T<T,T>
T<T>

但是,如果我没有 class CD 的实例,那么我需要显式调用正确的重载怎么办?这将如何完成?即,main() 包含:

int main() {
    foo<C<A,B> >();
    foo<D<A> >();
}

我尝试了一些 foo() 的重载,如下所示:

template <template<class, class> class TC>
void foo() {
    std::cout << "T<T,T>" << std::endl;
};

template <template<class> class TD>
void foo(){
    std::cout << "T<T>" << std::endl;
};

template <template<class, class> class TC, class TA, class TB>
void foo() {
    std::cout << "T<T,T>" << std::endl;
};

template <template<class> class TD, class TA>
void foo(){
    std::cout << "T<T>" << std::endl;
};

然而,这(以及我能想到的所有排列)只会导致如下所示(缩写)输出的一系列错误

prog.cpp: In function 'int main()':
prog.cpp:44:18: error: no matching function for call to 'foo()'
     foo<C<A,B> >();
                  ^
prog.cpp:44:18: note: candidates are:
prog.cpp:19:6: note: template<template<class, class> class TC> void foo()
 void foo() {
      ^
prog.cpp:19:6: note:   template argument deduction/substitution failed:
prog.cpp:24:6: note: template<template<class> class TD> void foo()
 void foo(){
      ^
prog.cpp:24:6: note:   template argument deduction/substitution failed:

我想做的事情是否允许?如果是这样,我哪里搞砸了?

---- 编辑 ----

正如 apple apple 指出的那样,如果我的 main() 如下:

int main() {
    foo<C, A, B>();
    foo<D, A>();
}

我得到了预期的输出。

然而,我的 real-world 案例最终变得更加复杂。我会在这里展开一点。遗留代码在 headers 其他地方定义了(数百个)typedefs:

typedef C<A, B> type_117;
typedef D<A>    type_252;

我正在处理的 class 是模板化的,并使用其中一个 typedef 作为模板参数进行了实例化。所以类似的东西:

template <class Type>
class Test
{
public:
   Test();
   SomeClass mSC;
}
Test::Test()
  : mSC(foo<Type>())
{
};

其中 Test 被实例化为

Test<type_117> aTest;

所以我一直在努力弄清楚如何为这种情况编写 foo()。在我 Test 的初始值设定项中调用 foo() 时,我是否能够 "decompose" 它生成 <C,A,B> 形式?还是我遇到了障碍,需要重新设计一些现有的框架?

考虑到函数禁止偏特化;所以很难按照你的要求去做。

apple apple (chenge the calling as foo<C, A, B>() 的建议很好,但是,如果你想保持原来的调用 (foo<C<A, B>>()),你可以使用偏特化的事实允许 structs/classes 并为仿函数创建部分特化;类似于

template <typename>
struct bar;

template <template<typename, typename> class Tc, typename Ta, typename Tb>
struct bar<Tc<Ta,Tb>>
 {
   void operator() ()
    { std::cout << "bar<Tc<Ta, Tb>>()" << std::endl; }
 };

template <template<typename> class Tc, typename Ta>
struct bar<Tc<Ta>>
 {
   void operator() ()
    { std::cout << "bar<Tc<Ta>>()" << std::endl; }
 };

问题(?)是,调用它,你不能调用 as bar<C<A,B>>() od bar<D<A>>() 但你必须添加几个括号:

bar<C<A,B>>()();
bar<D<A>>()();

bar<C<A,B>>{}();
bar<D<A>>{}();

我想仿函数解决方案也可以解决您问题的编辑部分的问题。

如果添加的一对括号有问题,您可以(按照 Jarod42 的建议(谢谢!))将调用包装在模板函数中,如下所示

template <typename T>
void bar ()
 { bar<T>{}(); }

因此您可以调用 bar<C<A, B>>() 函数并在专门的 bar<C<A, B>> 结构中管理调用。

另请注意 Jarod42 的解决方案:根据您的要求,您可以只开发 bar.

的部分专业化版本

-- 编辑 --

OP 问

I'm not that familiar with partial specialization; could you expand a bit on how what I was trying was?

专业化(部分和完全)是一个很大很大的话题。

举个例子,给大家一个思路。

给定一个模板class/struct

template <typename X, typename Y>
struct foo
 { };

您可以 部分 将其特化如下(通过示例)

template <typename X>
struct foo<X, X>
 { };

当特化维护模板变量时,或者您可以完全特化如下(通过示例)

template <>
struct foo<int, long>
 { };

其中所有模板参数都是固定的。

好吧:使用函数你可以完全专业化但不能部分专业化。

所以你可以写一个模板函数

 template <typename X, template Y>
 void foo ()
  { }

并完全专业化它

 template <>
 void foo<int, long> ()
  { }

但是你不能部分专业化它;所以你不能写(是一个错误)

 template <typename X>
 void foo<X, X> ()
  { }

您可以使用部分特化(和可变参数模板):

template <class Type>
class Test;

template <template <typename ...> class C, typename ... Ts>
class Test<C<Ts...>>
{
public:
   Test() : mSC(foo<C, Ts...>()) {}
   SomeClass mSC;
};
template<class T>struct tag_t{constexpr tag_t(){}};
template<class T>constexpr tag_t<T> tag{};

这些是类型标签。它们可以传递给没有类型实例的函数。

模板函数将推导它们。

template <template<class, class> class TC, class TA, class TB>
void foo(tag_t<TC<TA, TB>>) {
  std::cout << "T<T,T>" << std::endl;
};

template <template<class> class TD, class TA>
void foo(tag_t<TD<TA>>){
  std::cout << "T<T>" << std::endl;
};

在呼叫站点做 foo(tag<type_117>) 和鲍勃,正如他们所说,是你的叔叔。

在 C++98 中(恶心):

template<class T>struct tag_t{};
foo(tag_t<type_117>());