我想检查是否存在接受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中的错误,或者代码是在某个“实现定义”区域中,我不能假设它在所有平台上都有相同的行为?
答案 0 :(得分:4)
编译器之间的差异是由void_t
的定义引起的:
Is there a compiler bug exposed by my implementation of an is_complete type trait?
简而言之,标准不清楚别名模板特化中的未使用参数是否会导致替换失败或者被忽略。 CWG issue 1558的决议澄清了问题中void_t
的较短定义应该有效。
使用
解决了这个问题template<typename... Ts>
struct make_void { typedef void type;};
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;
§14.6.4.2[temp.dep.candidate]:
对于依赖于模板参数的函数调用, 使用通常的查找规则找到候选函数(3.4.1, 3.4.2,3.4.3)除了:
- 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,只有来自 找到模板定义上下文。
- 对于使用关联命名空间(3.4.2)的查找部分,只能在模板定义上下文中找到函数声明 或找到模板实例化上下文。
如果函数名称是 unqualified-id ,则调用将是 格式不正确或者会发现更好的匹配 关联的命名空间考虑了所有的函数声明 在所有翻译中在这些名称空间中引入的外部链接 单位,而不仅仅是考虑模板中的那些声明 定义和模板实例化上下文,然后程序有 未定义的行为。
Serialize
的非限定查询在模板定义上下文中执行,找不到Serialize(int &)
,并且int&
类型的参数没有ADL,因此10
是正确的输出。