【问题标题】:Why can't the compiler deduce the return type if argument type depends on a template parameter如果参数类型取决于模板参数,为什么编译器不能推断返回类型
【发布时间】:2020-09-24 01:07:53
【问题描述】:

我在尝试根据另一个模板参数在模板中选择重载集 (foo) 的函数时遇到模板推导错误:

#include <iostream>
#include <string>

void foo(int a){
    std::cout << "int";
}

void foo(double a) {
    std::cout << "double";
}

template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
    fun(data.value);   
}

struct IntData{
    using type = int;
    type value;
};

int main()
{
  IntData x{1};
  print(x, foo);
}

我收到以下错误:

In function 'int main()':
27:15: error: no matching function for call to 'print(IntData&, <unresolved overloaded function type>)'
27:15: note: candidate is:
15:6: note: template<class T, class R> void print(const T&, R (*)(typename T::type))
15:6: note:   template argument deduction/substitution failed:
27:15: note:   couldn't deduce template parameter 'R'

由于模板推导应该从左到右进行,我的假设是一旦推导T的类型,R的类型也应该是可推导的。实际上,我可以在调用 print 时摆脱错误

print<IntData>(x, foo);

这似乎表明,一旦知道T,实际上就可以推导出R。那么,如果两个参数都要推导,为什么它不起作用呢?

谢谢!

【问题讨论】:

  • 鸡,见蛋!如果某处有另一个print 模板怎么办?
  • 推论不会从左到右进行——对每个参数独立执行推论,然后整理结果,如果任何两个不同的参数成功但产生冲突的推论,则会出错。如果删除 const T&amp; 参数,您应该会得到完全相同的错误
  • @AsteroidsWithWings 不相关
  • @M.M 我不明白为什么不呢?有多个函数模板可供选择的潜力至少部分推动了演绎规则的构建方式,确定吗?
  • @AsteroidsWithWings 模板推导基于可见的内容(在声明和实例化上下文中),通常不可见的额外特化或重载会导致程序格式错误 NDR

标签: c++ templates template-argument-deduction


【解决方案1】:

我相信这是因为您将 R 作为函数指针参数的返回类型。

注意这句话from a previous question

所以,当我们询问函数的签名时,我们必须给出两个答案:

对于是函数模板的特化的函数, 签名包括返回类型

对于不是特化的函数,返回类型不是签名的一部分

由于foo 只是一个重载函数,而void 不是foo 函数签名的一部分,R帮助编译器推断出正确的函数重载。因此,使用foo 作为函数指针在main 范围内是不明确的。编译器通常通过匹配提供的参数的类型来解决重载,当函数指针本身时没有。

我相信这是最强大的解决方案,包括一个中间函数来解决以前不明确的函数指针。除了 int 之外,我还包括了一些其他类型,以展示将 auto 与下面提到的策略一起使用的灵活性。

#include <iostream>
#include <string>

void foo(int a){
    std::cout << "int" << std::endl;
}

void foo(double a) {
    std::cout << "double" << std::endl;
}

bool foo(char a) {
    std::cout << "char" << std::endl;
    return true;
}

template <typename T, typename R>
R print(const T& data, R(*fun)(typename T::type) ) {
    return fun(data.value);   
}

struct IntData{
    using type = int;
    type value;
};
struct DoubleData{
    using type = double;
    type value;
};
struct CharData{
    using type = char;
    type value;
};
template <typename T>
auto print2(const T& data)
{
  auto(*fooResolved)(typename T::type) = foo;
  return print(data,fooResolved);
}

int main()
{
  IntData x{1};
  print2(x);

  DoubleData y{1.0};
  print2(y);

  CharData z{'a'};
  bool result = false;
  std::cout << "bool before: " << result << std::endl;
  result = print2(z);
  std::cout << "bool after : " << result << std::endl;
}

这里还有一些潜在的解决方案可以帮助说明问题:

(注意更改是删除 R 作为第二个模板参数)

#include <iostream>
#include <string>

void foo(int a){
    std::cout << "int";
}

void foo(double a) {
    std::cout << "double";
}

template <typename T>
void print(const T& data, void(*fun)(typename T::type) ) {
    fun(data.value);   
}

struct IntData{
    using type = int;
    type value;
};

int main()
{
  IntData x{1};
  print(x, foo);
}

还有这个(直接传值,允许多种返回类型)

#include <iostream>
#include <string>

void foo(int a){
    std::cout << "int";
}

void foo(double a) {
    std::cout << "double";
}

template <typename T, typename R>
void print(const T& data, R (*fun)(T) ) {
    fun(data);   
}

struct IntData{
    using type = int;
    type value;
};

int main()
{
  IntData x{1};
  print(x.value, foo);
}

并进一步说明原始问题(参见现在推导的返回类型)

#include <iostream>
#include <string>

void foo(int a){
    std::cout << "int" << std::endl;
}

bool foo(double a) {
    std::cout << "double" << std::endl;
    return true;
}

template <typename T, typename R>
R print(const T& data, R (*fun)(T) ) {
    return fun(data);   
}

struct IntData{
    using type = int;
    type value;
};
struct DoubleData{
    using type = double;
    type value;
};

int main()
{

  IntData x{1};
  print(x.value, foo);

  //foo(int) does not return a value
  //bool test = print(x.value, foo); // Does not compile

  DoubleData y{1.0};

  bool result = false;
  result = print(y.value, foo);
  std::cout << result << std::endl;

}

当我们这样做的时候,你也可以通过明确指定你想要的 foo 来解决它们给定的原始代码

#include <iostream>
#include <string>

void foo(int a){
    std::cout << "int";
}

void foo(double a) {
    std::cout << "double";
}

template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
    fun(data.value);   
}

struct IntData{
    using type = int;
    type value;
};

int main()
{
  IntData x{1};
  void(*fooResolved)(int) = foo;
  print(x, fooResolved);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-09
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多