正在检查是否存在接受T参数的非成员函数

Checking if non-member function that accepts T param exists

本文关键字:参数 成员 函数 检查 是否 存在      更新时间:2023-10-16

我想检查是否存在接受T参数类型的非成员函数。为了做到这一点,我使用了Walter E.Brown先生在cppcon上提出的void_t"技巧"(同样的技巧可以毫无问题地检查成员类型或成员函数是否存在)。

#include <iostream>
#include <type_traits>
template<typename...>
using void_t = void;
void Serialize(float&)
{
}
template<typename T, typename = void>
struct has_external_serialize : std::false_type
{
};
template<typename T>
struct has_external_serialize<T, void_t<decltype(Serialize(std::declval<T&>()))>> : std::true_type
{
};
void Serialize(int&)
{
}
int main(int argc, const char * argv[])
{
    std::cout<<has_external_serialize<float>::value<<has_external_serialize<int>::value;
}

当使用GCC编译时,此代码打印11,当使用clang(xcode 5.1.1)编译时,打印10

我的问题是——这个代码正确吗?如果是,clang或GCC中是否存在错误,或者代码位于某个"实现定义"区域,我不能假设它在所有平台上都会有相同的行为?

编译器之间的差异是由void_t的定义引起的:我实现的Is_complete类型特征是否暴露了编译器错误?简而言之,该标准不清楚别名模板专门化中未使用的参数是否会导致替换失败或被忽略。《化学武器公约》第1558号问题的决议阐明,该问题中void_t的较短定义应该有效。

使用解决该问题

template<typename... Ts>
struct make_void { typedef void type;};
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;

两个编译器都产生CCD_ 6。

§14.6.4.2[临时部门日期]:

对于依赖于模板参数的函数调用使用通常的查找规则(3.4.1,3.4.2、3.4.3),但除外

  • 对于使用非限定名称查找(3.4.1)或限定名称查找的查找部分找到模板定义上下文
  • 对于使用关联名称空间的查找部分(3.4.2),只有在模板定义上下文中找到的函数声明或者找到模板实例化上下文

如果函数名是不合格的id,则调用将为格式错误,或者如果在关联的命名空间考虑了具有在所有翻译中在这些名称空间中引入的外部链接单位,而不仅仅是考虑模板中的声明定义和模板实例化上下文,则程序具有未定义的行为。

Serialize的不合格查找是在模板定义上下文中执行的,不会找到Serialize(int &),并且类型为int&的参数没有ADL,因此10是正确的输出。