【问题标题】:C++ Type deduction on template specialization fails on void parametervoid 参数上模板特化的 C++ 类型推导失败
【发布时间】:2021-11-01 07:11:51
【问题描述】:

我创建了一个模板类,其中构造函数接受一个 std::function 对象。 第一个模板参数表示该函数的返回值。 第二个参数定义了该函数的参数类型。

#include <functional>

//Base
template<class R, class Arg>
class Executor {
    public:      
        Executor(std::function<R(Arg)> function) 
            : mFunction(function)  
        {}

    private:
        std::function<R(Arg)> mFunction;
};

//Specialization1
template<class Arg>
class Executor<void, Arg> {
    public:      
        Executor(std::function<void(Arg)> function) 
            : mFunction(function)  
        {}

    private:
        std::function<void(Arg)> mFunction;
};

//Specialization2
template<class R>
class Executor<R, void> {
    public:      
        Executor(std::function<R()> function) 
            : mFunction(function)  
        {}

    private:
        std::function<R()> mFunction;
};

int testBase(float value) {
    return 5;
}

void testSpecialization1(float value) {}

int testSpecialization2() {
    return 22;
}

int main() {
    Executor executorBase{std::function(testBase)};
    
    Executor executorSpecialization1{std::function(testSpecialization1)};
    
    //Executor<int, void> executorSpecialization2{std::function(testSpecialization2)}; // Compiles
    Executor executorSpecialization2{std::function(testSpecialization2)}; // Doesn't compile. Uses Base. template<class R, class Arg>
}

在使用函数 std::function 时,请参阅 executorSpecialization2,编译器会抱怨:

main.cpp: In function 'int main()':
main.cpp:55:72: error: class template argument deduction failed:
   55 |     Executor executorSpecialization2{std::function(testSpecialization2)}; // Doesn't compile. Uses Base. template<class R, class Arg>
      |                                                                        ^
main.cpp:55:72: error: no matching function for call to 'Executor(std::function<int()>)'
main.cpp:7:9: note: candidate: 'template<class R, class Arg> Executor(std::function<R(Arg)>)-> Executor<R, Arg>'
    7 |         Executor(std::function<R(Arg)> function)
      |         ^~~~~~~~
main.cpp:7:9: note:   template argument deduction/substitution failed:
main.cpp:55:72: note:   candidate expects 1 argument, 0 provided
   55 |     Executor executorSpecialization2{std::function(testSpecialization2)}; // Doesn't compile. Uses Base. template<class R, class Arg>
      |                                                                        ^
main.cpp:5:7: note: candidate: 'template<class R, class Arg> Executor(Executor<R, Arg>)-> Executor<R, Arg>'
    5 | class Executor {
      |       ^~~~~~~~
main.cpp:5:7: note:   template argument deduction/substitution failed:
main.cpp:55:72: note:   'std::function<int()>' is not derived from 'Executor<R, Arg>'
   55 |     Executor executorSpecialization2{std::function(testSpecialization2)}; // Doesn't compile. Uses Base. template<class R, class Arg>
      |

它尝试使用基本版本。但当然缺少第二个参数。 如果我指定它编译的模板参数。

那么为什么要为 executorSpecialization2 选择基本模板呢? 是否甚至可以在不需要传递模板参数的情况下对 void 使用类型推导?

谢谢

【问题讨论】:

标签: c++ templates c++17


【解决方案1】:

int(void) 只能在狭窄的情况下用作int()

改成这样:

template<class R, class... Args>
class Executor

并将正文中的Arg 替换为Args...

取消R, void 专业化。

如果您需要class Executor&lt;void, Arg&gt;,请使用class Executor&lt;void, Args...&gt;

您的代码现在将编译一个作品。

(为什么会出现错误:隐式生成的推导指南仅基于主要专业化。std::function(testSpecialization2)std::function&lt;int()&gt;,它与主要专业化的任何构造函数都不匹配。)

【讨论】:

  • 感谢您的回答。我使用了“演绎指南”的方法。使用参数包的方法我稍后必须整理出给定的类型,因此使用专业化是解决我的问题的更简单的方法。
【解决方案2】:

当您使用名称 Executor 而没有像 Executor&lt;int, float&gt; 中的显式模板参数时,C++ 通常会尝试弄清楚您所说的 class template argument deduction(或 CTAD)是什么意思。此过程不查看任何类模板特化(部分或显式),只查看主模板。

主模板具有构造函数Executor(std::function&lt;R(Arg)&gt; function);。这足以确定RArg 类型为std::function&lt;int(float)&gt;std::function&lt;void(float)&gt; 的参数。这只是为了确定 RArg 对于 Executor 是什么,然后适用专业化的正常考虑,因此第二个对象确实使用了 Executor&lt;void, Arg&gt; 专业化。

但是在看到std::function&lt;int()&gt; 类型时,它与std::function&lt;R(Arg)&gt; 不匹配,因此 CTAD 失败。 (虽然你可以拼写一个空的函数参数列表(void)() 的含义相同,但这是一个特殊规则,它需要一个非依赖的void 类型,并且不适用于恰好具有@ 类型的模板参数987654339@.)

但你可以通过编写“演绎指南”来协助 CTAD:

template <class R>
Executor(std::function<R()>) -> Executor<R, void>;

除主模板的构造函数外,还使用演绎指南。现在std::function&lt;int()&gt; void 类型的参数匹配推导指南,编译器确定R 应该是int,并使用Executor&lt;int, void&gt;,这是由第二部分特化定义的。

看到它工作on coliru

【讨论】:

  • 谢谢。 “演绎指南”的解决方案是我需要的解决方案。
猜你喜欢
  • 1970-01-01
  • 2015-10-11
  • 1970-01-01
  • 2022-01-06
  • 1970-01-01
  • 2021-07-01
  • 2020-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多