指针与nullptr_t的模板参数类型推导

问题描述

请考虑以下C ++程序:

#include <cstdlib>
#include <functional>
#include <iostream>
#include <string>
#include <utility>

namespace {
template <typename Result,typename... Arg>
Result call_fn(std::function<Result(Arg...)> fn,Arg&&... arg) {
    return fn(std::forward<Arg>(arg)...);
}

std::string test_fn(int,std::string*) {
    return "hello,world!";
}
}

int main(int,char**) {
    std::cout << call_fn(std::function(test_fn),nullptr) << "\n";

    return EXIT_SUCCESS;
}

这是一个人为的示例,但是在尝试实现与std::make_unique类似的东西时遇到了相同的问题。编译该程序时,出现以下错误

$ clang++ -std=c++17 -Wall -Weverything -Werror -Wno-c++98-compat call_fn.cpp
call_fn.cpp:19:18: error: no matching function for call to 'call_fn'
    std::cout << call_fn(std::function(test_fn),nullptr) << "\n";
                 ^~~~~~~
call_fn.cpp:9:8: note: candidate template ignored: deduced conflicting types for parameter 'Arg' (<int,std::__cxx11::basic_string<char> *> vs. <int,nullptr_t>)
Result call_fn(std::function<Result(Arg...)> fn,Arg&&... arg) {
       ^
1 error generated.

问题似乎是指针参数的类型推导为nullptr_t,而不是std::string*。我可以通过添加static_cast或为call_fn显式指定模板参数来“修复”此问题,但是我发现这种冗长的级别令人讨厌。

是否有一种方法可以修改call_fn的定义,以便类型推导对指针参数更有效?

解决方法

问题在于,一方面Arg...定义了std::function<...>的推导规则,另一方面,输入参数定义了它。

您应该只使用std::invoke而不是call_fn或使用typename... Args2作为输入参数。或者,您可以放弃输入为std::function的要求,而只接受任何可调用对象。

另一种选择是确保输入参数不用于参数类型推导,但我不确定如何使用可变参数模板(typename...)-对语法有疑问。

编辑:让我写一个例子

     template<typename T> foo(std::vector<T> x,T y);

     std::vector<double> x;
     int y;
     foo(x,y);

由于冲突,此处foo将无法推断T。解决此问题的方法之一是确保类型推导中不使用y

    template<typename T> 
    struct same_type_impl { type=T;};

    template<typename T>
     using same_type = typename same_type_impl<T>::type;

    template<typename T> foo(std::vector<T> x,same_type<T> y);

     std::vector<double> x;
     int y;
     foo(x,y);

这里foo会推论Tdouble

,

根据我的测试,我认为std::function是罪魁祸首。对可变参数模板的类型太严格了(我认为)。

以下作品:

#include <iostream>
#include <string>
#include <utility>

namespace {

template <typename FnT,typename... Arg>
auto call_fn(FnT fn,Arg&&... arg) {
    return fn(std::forward<Arg>(arg)...);
}

std::string test_fn(int,std::string*) { return "hello,world!"; }

}  // namespace

int main(int,char**) {
    std::cout << call_fn(test_fn,nullptr) << "\n";

    return EXIT_SUCCESS;
}

像这样,我们只是模板化std::function,现在唯一的要求是我们提供的函数接受这些参数并且可以调用(operator())。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...