【问题标题】:overload of template template function - explicit call模板模板函数的重载 - 显式调用
【发布时间】:2017-03-30 17:20:29
【问题描述】:

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

#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>

但是,如果我没有 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>();
}

我得到了预期的输出。

但是,我的真实案例最终变得更加复杂。我将在这里扩展一点。遗留代码在其他地方的标头中定义了(数百个)typedef:

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

我正在处理的类是模板化的,并使用这些 typedef 之一作为模板化参数进行实例化。所以大致如下:

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

其中 Test 被实例化为

Test<type_117> aTest;

所以我一直在试图弄清楚如何为这种情况编写foo()。在我在Test 的初始化程序中调用foo() 时,我是否能够“分解”它以生成&lt;C,A,B&gt; 表单?还是我遇到了障碍,需要重新设计一些现有框架?

【问题讨论】:

  • 不是foo&lt;C,A,B&gt;(); 吗?
  • 这似乎确实有效......但会导致后续问题 - 我将编辑原件

标签: c++ templates template-templates


【解决方案1】:
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&lt;type_117&gt;) 和鲍勃,正如他们所说,是你的叔叔。

在 C++98 (ick) 中:

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

【讨论】:

  • 这看起来很吸引人,但看起来它只适用于 c++11 并且无法避免使用 constexpr。我没有说明我(还)不能过渡到新功能是我的错。不过会牢记未来的努力!
  • @Khanmots 不,不需要constexpr;从类型中删除它。模板变量tag 是可选的,只需将use 替换为tag_t&lt;type_117&gt;()。在实践中,考虑使用比 19 年前更新的 C++ 标准。 ;) 编辑为答案。
  • 我会试一试。拥有大量遗留代码库的大型组织变化缓慢......一直在尝试:)
【解决方案2】:

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

template <class Type>
class Test;

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

【讨论】:

    【解决方案3】:

    考虑到函数的部分特化是被禁止的;所以很难做到你到底问了什么。

    apple 的建议(将调用称为foo&lt;C, A, B&gt;() 是一个很好的建议,但是,如果你想保持原来的调用(foo&lt;C&lt;A, B&gt;&gt;()),你可以使用结构/类允许部分特化的事实并为仿函数创建部分特化;类似

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

    问题 (?) 是,调用它时,你不能调用 bar&lt;C&lt;A,B&gt;&gt;() od bar&lt;D&lt;A&gt;&gt;() 但你必须添加几个括号:

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

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

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

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

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

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

    还请注意 Jarod42 的解决方案:根据您的要求,您可以只开发 bar 的部分专业化版本。

    -- 编辑--

    OP 询问

    我对部分专业化不是很熟悉;你能详细说明我的尝试是什么吗?

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

    只是一些例子,给出一个想法。

    给定一个模板类/结构

    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 &lt;typename T&gt; void bar2() { bar&lt;T&gt;{}(); } 并调用bar2 而不是bar
    • 我会避免将包装器命名为 foo,因为 OP 也使用 foodetail_barbar会是更好的名字。
    • @Jarod42 - 好的; bar().
    猜你喜欢
    • 2017-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 2011-01-11
    相关资源
    最近更新 更多