Евг*_*цын 5 c++ function-templates overload-resolution template-meta-programming
我有以下小的 C++ 代码示例,但我无法弄清楚为什么编译器以这种方式工作,尽管我花了很多时间研究 cppreference。我将不胜感激任何解释!神箭
#include <type_traits>
template<typename Tp>
struct depend_type
{
constexpr static bool false_ = false;
};
template<typename Tp>
struct cont
{
using x = void;
static_assert(std::is_same_v<Tp, int>);
// if uncomment, will be complie error -> 'cont<Tp>::x' instantiated with 'void (*p)(int) = func;'
// static_assert(depend_type<Tp>::false_);
};
template<typename Tp>
void func(Tp)
{
}
template<typename Tp>
typename cont<Tp>::x func(Tp);
int main(int /* argc */, char * /*argv*/[])
{
// func(1); is ambiguous call
void (*p)(int) = func; // why is not ambiguous?
return 0;
}
Run Code Online (Sandbox Code Playgroud)
void (*p)(int) = func;重载解析与 类似func(1),但有一个关键区别。
调用和赋值都会首先查找func并找到两个函数模板。模板参数推导将在两个模板上进行,Tp = int并对它们进行推导。
然后Tp将被替换到模板中。这就是为什么你static_assert(depend_type<Tp>::false_);会触发,因为编译器需要弄清楚typename cont<Tp>::x第二个函数的返回类型是什么。
然后需要找到最佳可行的函数。对于调用 ( func(1)) 中的重载决策,两个重载都不比另一个重载更好(两者都采用int来自 的参数Tp)。没有其他东西可以比较这两个重载,因此它是不明确的。
但在赋值情况(void (*p)(int) = func;)中,还需要考虑返回类型。在第一个函数模板中,void不是来自模板,因此它比第二个函数模板更好,其中void是依赖类型。所以选择第一个函数模板。
用一个不同的例子可能会更容易理解:
template<class T>
T** f() {
std::cout << "T**\n";
return nullptr;
}
template<class T>
T* f() {
std::cout << "T*\n";
return nullptr;
}
template<class T>
T f() {
std::cout << "T\n";
return T{};
}
int main() {
// void** p = f<void>(); // Doesn't compile: return type doesn't participate in ranking most viable function
// void** is a better match for T** than T* or T (it's more specialized)
static_cast<void**(&)()>(f)(); // T**
// void* is a better match for T* than T
static_cast<void*(&)()>(f)(); // T*
// T = void for the third function template is the only option
static_cast<void(&)()>(f)(); // T
}
Run Code Online (Sandbox Code Playgroud)
这是在重载决策期间考虑函数返回类型的三种情况之一。第二个是类型转换运算符返回类型,第三个是显式(完全)专业化期间的类型。