【问题标题】:Call template specialization with more parameters for specific value of one-parameter template call具有更多参数的调用模板专业化,用于单参数模板调用的特定值
【发布时间】:2020-04-09 03:26:04
【问题描述】:

我有一个带有 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"
}

我想我将不得不做一个接受 2 个参数的专业化,第一个是 Foo1,如下所示:

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

但是我如何进行只接受一个参数Foo1 并导致SomeClass&lt;Foo1,Foo2&gt; 的特化?

【问题讨论】:

    标签: c++ c++11 templates variadic-templates template-specialization


    【解决方案1】:
    template <>
    struct SomeClass<Foo1>:
      SomeClass<Foo1,Foo2>
    {};
    

    这将解决您描述的 90% 的问题。如果您需要SomeClass&lt;Foo1&gt;SomeClass&lt;Foo1,Foo2&gt; 相同而不是继承自SomeClass&lt;Foo1,Foo2&gt;,您必须编写一堆别名和::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&lt;Foo1, Foo2&gt; 获得::typeFake 看不到中间的Helper&lt;Foo1&gt;,所以我们从Fake&lt;Foo1&gt; 得到的类型是Real&lt;Foo1, Foo2&gt;,而不是派生类。

    这确保std::is_same&lt; Fake&lt;Foo1&gt;, Fake&lt;Foo1, Foo2&gt; &gt;

    Fake 是用户使用的别名模板“API”。 Helper 让您可以根据传入的参数来选择您实例化的 Real 模板。

    【讨论】:

    • 谢谢。如果您有一个使用一堆别名和::type 噪音的示例,我仍然会对学习感兴趣=)
    • 添加了@plg 噪音。
    • @Adam 非常感谢。我花了一段时间才把头绕过去。如果您经常进行这种模板参数推断,我认为这根本不是一个不雅的解决方案。在我现在正在做的项目中,我肯定会这样做。
    【解决方案2】:

    对于类模板的情况,最简单的解决方案可能是定义一个与您感兴趣的情况相匹配的特化,并通过继承自它:

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

    【讨论】:

    • 您不能专门化别名模板。替代方法不起作用。 godbolt.org/z/SfabM6
    • 谢谢!第一个解决方案效果很好。我不认为为此使用继承。我会喜欢第二个工作的,但我收到了error: expected unqualified-id before 'using'
    • @MaxLanghof 我不知道是什么击中了我。我发誓我在发布之前在 Godbolt 上尝试过这个,它看起来像是编译过的......
    • @MichaelKenzel Godbolt 今天对我来说有点奇怪,所以也许这就是你的原因......
    • @MaxLanghof 我什至对它会起作用感到惊讶,因为我记得因为不能部分专门化别名模板而被惹恼了。但我认为这只是一个完整的专业化......
    【解决方案3】:

    如果您想避免 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
    

    【讨论】:

    • 谢谢。我今天晚些时候去看看
    猜你喜欢
    • 2011-12-07
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 2017-09-10
    • 2021-01-24
    相关资源
    最近更新 更多