为什么编译器不能推断出返回类型?

时间:2019-03-22 00:06:37

标签: c++ templates

如果返回类型碰巧是模板参数,编译器是否不能推断出函数的返回类型?否则我在以下代码中出错。

#include <iostream>

template <typename TT>                                                                                                                                                                                                                                                                                                                                                                        
TT retBoolFail(bool a) {                                                                                                                                                                                                                                                                                                                                                                      
    return true;                                                                                                                                                                                                                                                                                                                                                                              
}

template <typename TT>                                                                                                                                                                                                                                                                                                                                                                        
void retBoolSuccess(bool a, TT& ret) {                                                                                                                                                                                                                                                                                                                                                        
    ret = true;                                                                                                                                                                                                                                                                                                                                                                               
    return;                                                                                                                                                                                                                                                                                                                                                                                   
}

int main() {                                                                                                                                                                                                                                                                                                                                                                                  
    bool ret;                                                                                                                                                                                                                                                                                                                                                                                 
    retBoolSuccess(true, ret);  // Success                                                                                                                                                                                                                                                                                                                                                    
    retBoolFail(true);          // Failure                                                                                                                                                                                                                                                                                                                                                    
    return 0;                                                                                                                                                                                                                                                                                                                                                                                 
}

第'RetBoolFail(true)行失败,并出现以下错误。

-*- mode: compilation; default-directory: "~/work/c++/tupleTemplate/" -*-
Compilation started at Thu Mar 21 16:52:50

g++ -c simpletemp.cc  -std=c++1z -g; g++ -o simpletemp simpletemp.o
simpletemp.cc: In function ‘int main()’:
simpletemp.cc:17:21: error: no matching function for call to ‘retBoolFail(bool)’
     retBoolFail(true);          // Failure
                     ^
simpletemp.cc:4:4: note: candidate: template<class TT> TT retBoolFail(bool)
 TT retBoolFail(bool a) {
    ^
simpletemp.cc:4:4: note:   template argument deduction/substitution failed:
simpletemp.cc:17:21: note:   couldn't deduce template parameter ‘TT’
     retBoolFail(true);          // Failure
                     ^
Compilation finished at Thu Mar 21 16:52:50

谢谢。

3 个答案:

答案 0 :(得分:3)

否,仅在参数列表中进行推导。您可以改用auto,也可以仅使其成为具有硬编码返回类型的非模板函数。

答案 1 :(得分:1)

类型推导不适用于仅用于函数模板参数的返回类型。

您必须使用

retBoolFail<bool>(true);

您必须提示编译器推断返回类型的类型。即使在函数模板中使用auto仍然需要您在调用站点提示编译器。

答案 2 :(得分:1)

将模板名称用作要调用的函数时,模板参数推导仅使用函数参数和模板函数签名,而不使用函数主体。

在标准中很难更改此规则的一个原因是,如果名称重载,事情将如何进行。完整的图片如下:

  1. 名称查找可找到一组函数和函数模板。

  2. 对于集合中的每个功能模板,模板参数均由显式模板参数,推导和/或默认模板参数确定。如果推导失败,或者如果将确定的模板参数替换为函数类型导致无效,则该函数模板将从重载集中丢弃为不可行。

  3. 对于每个函数签名(无论是否来自模板),都将进行检查以查看函数参数和函数参数是否匹配。如果不是,则该功能不可行,并从重载集中丢弃。

  4. 过载分辨率比较其余可行的功能。对于大多数过程,模板中的函数类型与非模板函数一样,但是有一些针对函数模板专业化的最终决胜规则。

  5. 如果重载解析选择了功能模板特化,并且上下文是需要存在功能定义的上下文,则功能实例也将实例化以生成该定义。如果将模板参数参数替换为函数主体会导致某些无效操作,则程序格式错误。

因此,在实例化函数模板的函数类型和是否实例化时,以及实例化函数主体时,以及在两种情况下无效模板参数使用的结果之间,存在区别。从功能体中推论会混淆这一点。

但是在某些其他情况下,函数模板返回类型中的模板参数可以参与模板参数推导。 (此列表可能并不完整。)

如果从函数模板名称(或其指针大小写的地址)初始化了指向函数的指针或对函数的引用,则模板参数推导涉及返回类型:

template <typename TT>
TT f();

unsigned int (&func_ptr)() = f; // TT deduced as unsigned int

int g(double (*)());
int g_of_f = g(f);  // TT deduced as double

一个类或类模板可以具有转换功能模板。然后,具有该类类型的表达式的某些用法可以隐式使用该模板,这需要在其返回类型中推导模板参数(这也是其声明中operator关键字之后的内容)。

class A {
public:
    template <typename TT>
    operator std::shared_ptr<TT>() const;
};

std::shared_ptr<int> p = A{}; // TT deduced as int

还要注意,使用“占位符类型”作为其返回类型的函数或函数模板(该返回类型包含关键字auto)的确从主体的return语句(即不会因为“ if constexpr”而被跳过)。尽管没有模板头的函数仍然只是具有一种特定类型的一个函数,而不是函数模板,并且这种推论是与任何模板参数推论分开的步骤。

auto retBool(bool a) {
    return true;       // return type is bool
}

template <typename T>
constexpr const auto* constify(T* ptr) {
    return ptr;       // return type is const T* (T* if T is already const)
}