为单参数模板调用的特定值调用具有更多参数的调用模板特化

Call template specialization with more parameters for specific value of one-parameter template call

我有一个带有 2 个参数的模板。对于第一个参数的某个值,我知道第二个参数应该是什么。我希望我的模板只有一个完整定义(带有 2 个参数的那个),并且能够仅提供一个参数来实例化我的模板。

在下面的例子中,我知道如果第一个模板参数是Foo1,那么第二个应该是Foo2。我希望能够通过编写 Someclass<Foo1>.

来创建 Someclass<Foo1,Foo2>
#include <iostream>
using namespace std;

struct Foo1 { Foo1() { cout << "Foo1 "; }};
struct Foo2 { Foo2() { cout << "Foo2 "; }};

template <typename ...Dummy> struct SomeClass;
template <typename T, typename U> struct SomeClass<T,U> {
  SomeClass() {
    T t;
    U u;
  }
};

/* Here, some one-argument specialization where if SomeClass<Foo1> is desired,
 * SomeClass<Foo1, Foo2> is obtained. */

int main() {
  SomeClass<Foo1, Foo2> c; //prints "Foo1 Foo2 "
  SomeClass<Foo1> c2;      //Should print the same thing, right now "incomplete type"
}

我想我将不得不做一个有两个参数的特化,第一个是 Foo1,像这样:

template <typename U> struct SomeClass<Foo1, U> {
  SomeClass() {
    Foo1 f;
    U u;
  }
};

但是我如何做一个只接受一个参数 Foo1 并产生 SomeClass<Foo1,Foo2> 的特化?

template <>
struct SomeClass<Foo1>:
  SomeClass<Foo1,Foo2>
{};

这将处理您描述的 90% 的问题。如果你需要 SomeClass<Foo1>SomeClass<Foo1,Foo2> 相同,而不是继承自 SomeClass<Foo1,Foo2>,你必须写一堆别名和 ::type 噪音。

template<class...Args>
struct Real;

template<class...Args>
struct Helper {
  using type=Real<Args...>;
};

template<class...Args>
using Fake = typename Helper<Args...>::type;

这 3 层是噪声。

Helper 让您重新定义参数的含义。

template <typename T, typename U>
struct Real<T,U> {
  Real() {
    T t;
    U u;
  }
};

template<>
struct Helper<Foo1>:Helper<Foo1, Foo2> {};

我们从这里的 Helper<Foo1, Foo2> 得到 ::typeFake 没有看到中间 Helper<Foo1>,所以我们从 Fake<Foo1> 得到的类型是 Real<Foo1, Foo2>,而不是派生的 class.

这确保了 std::is_same< Fake<Foo1>, Fake<Foo1, Foo2> >.

Fake 是用户使用的别名模板 "API"。 Helper 允许您进行一些专门化,以根据传入的参数选择要实例化的 Real 模板。

对于 class 模板的情况,最简单的解决方案可能是定义一个与您感兴趣的案例相匹配的专业化,并将 "forward" 与您的参数相匹配希望通过继承它来映射到:

template <typename ...Dummy>
struct SomeClass
{
    // default implementation
};

template <typename T, typename U>
struct SomeClass<T, U>
{
    // case for two parameters
};

template <> struct SomeClass<Foo1> : public SomeClass<Foo1, Foo2> {};

如果你想避免 Yakk 提出的继承解决方案,你可以尝试使用第二个模板参数,其默认类型取决于第一个参数

我的意思是

template <typename T, typename U = typename DepDef<T>::type>
struct SomeClass

其中 DepDef 是声明的模板结构

template <typename>
struct DepDef;

并且 Foo1 的默认值通过模板特化定义如下

template <>
struct DepDef<Foo1>
 { using type = Foo2; };

例如,如果您希望 Foo3 的默认类型是 Foo4,您可以添加以下特化

template <>
struct DepDef<Foo3>
 { using type = Foo4; };

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

#include <iostream>
#include <functional>

struct Foo1 { Foo1() { std::cout << "Foo1 "; } };
struct Foo2 { Foo2() { std::cout << "Foo2 "; } };
struct Foo3 { Foo3() { std::cout << "Foo3 "; } };
struct Foo4 { Foo4() { std::cout << "Foo4 "; } };

template <typename> struct DepDef;

template <> struct DepDef<Foo1> { using type = Foo2; };
template <> struct DepDef<Foo3> { using type = Foo4; };

template <typename T, typename U = typename DepDef<T>::type>
struct SomeClass
 { T t; U u; };

int main ()
 {
   SomeClass<Foo1, Foo2>  f0;
   SomeClass<Foo1, Foo3>  f1;
   SomeClass<Foo1>        f2; // default Foo2
   SomeClass<Foo3, Foo4>  f3;
   SomeClass<Foo3, Foo1>  f4;
   SomeClass<Foo3>        f5; // default Foo4
   SomeClass<Foo2, Foo1>  f6;  
   //SomeClass<Foo2>        f7; // compilation error: no default defined for Foo2
 }

观察到 f7 定义(最后一个)失败,因为没有为 Foo2.

定义 DepDef 专业化

如果您希望有一个通用默认值(未定义 DefDep 特化时的默认值),您可以在声明中定义它

template <typename>
struct DepDef
 { type = void; }; // generic default