C++ 模板的函数解析是如何完成的?
How is function resolution done for C++ templates?
让我困惑的代码贴在这里:
namespace ns1 {
struct myStruct1 {};
struct myStruct2 {};
}
namespace ns2 {
template <typename T>
constexpr int foo(T& x) {
return 1;
}
// If the two functions below are switched, it returns 2 correctly
template <typename T>
constexpr int fooCaller(T& x) {
return foo(x);
}
constexpr int foo(ns1::myStruct2& x) {
return 2;
}
}
// If the below is uncommented, it also returns 2 correctly
/*
namespace ns1 {
constexpr int foo(myStruct2& x) {
return 2;
}
}
*/
int main() {
ns1::myStruct1 struct1;
constexpr int struct1Foo1 = ns2::foo(struct1);
static_assert(struct1Foo1 == 1);
constexpr int struct1Foo2 = ns2::fooCaller(struct1);
static_assert(struct1Foo2 == 1);
ns1::myStruct2 struct2;
constexpr int struct2Foo1 = ns2::foo(struct2);
static_assert(struct2Foo1 == 2);
constexpr int struct2Foo2 = ns2::fooCaller(struct2);
static_assert(struct2Foo2 == 2); // Assertion fails, returns 1 instead!
}
我正在尝试重载模板函数 (foo
)。如果我没理解错的话,模板代码只会在调用函数时生成。到那时,应该已经声明了函数的重载版本(正如您在代码中看到的那样)并且名称查找应该已经在该重载版本上进行了选择。
我确定已经定义了重载版本,因为static_assert(struct2Foo1 == 1)
returns True
,这表明已经定义了foo(ns1::myStruct2&)
。
另一个令人费解的事情是将重载版本放在 namespace ns1
中会导致模板函数选择重载版本。我知道这可能是由于 ADL 而发生的,但我不确定为什么 ADL 在同一命名空间中直接重载时不能工作。
那么,当我将重载版本放在同一命名空间中的模板声明下方时,为什么它没有选择重载版本?
从属名称的普通非限定查找仅考虑在模板定义上下文中找到的声明。因此找不到 ns2
中的第二个 foo
。
依赖于参数的查找将考虑在定义或实例化上下文中找到的声明,但它只查找名称空间和与参数关联的 类,此处为 ns1
。因此,将找到稍后在 ns1
中声明的 foo
,但不会在 ns2
.
中找到
让我困惑的代码贴在这里:
namespace ns1 {
struct myStruct1 {};
struct myStruct2 {};
}
namespace ns2 {
template <typename T>
constexpr int foo(T& x) {
return 1;
}
// If the two functions below are switched, it returns 2 correctly
template <typename T>
constexpr int fooCaller(T& x) {
return foo(x);
}
constexpr int foo(ns1::myStruct2& x) {
return 2;
}
}
// If the below is uncommented, it also returns 2 correctly
/*
namespace ns1 {
constexpr int foo(myStruct2& x) {
return 2;
}
}
*/
int main() {
ns1::myStruct1 struct1;
constexpr int struct1Foo1 = ns2::foo(struct1);
static_assert(struct1Foo1 == 1);
constexpr int struct1Foo2 = ns2::fooCaller(struct1);
static_assert(struct1Foo2 == 1);
ns1::myStruct2 struct2;
constexpr int struct2Foo1 = ns2::foo(struct2);
static_assert(struct2Foo1 == 2);
constexpr int struct2Foo2 = ns2::fooCaller(struct2);
static_assert(struct2Foo2 == 2); // Assertion fails, returns 1 instead!
}
我正在尝试重载模板函数 (foo
)。如果我没理解错的话,模板代码只会在调用函数时生成。到那时,应该已经声明了函数的重载版本(正如您在代码中看到的那样)并且名称查找应该已经在该重载版本上进行了选择。
我确定已经定义了重载版本,因为static_assert(struct2Foo1 == 1)
returns True
,这表明已经定义了foo(ns1::myStruct2&)
。
另一个令人费解的事情是将重载版本放在 namespace ns1
中会导致模板函数选择重载版本。我知道这可能是由于 ADL 而发生的,但我不确定为什么 ADL 在同一命名空间中直接重载时不能工作。
那么,当我将重载版本放在同一命名空间中的模板声明下方时,为什么它没有选择重载版本?
从属名称的普通非限定查找仅考虑在模板定义上下文中找到的声明。因此找不到 ns2
中的第二个 foo
。
依赖于参数的查找将考虑在定义或实例化上下文中找到的声明,但它只查找名称空间和与参数关联的 类,此处为 ns1
。因此,将找到稍后在 ns1
中声明的 foo
,但不会在 ns2
.