【问题标题】:Checking if non-member function that accepts T param exists检查接受 T 参数的非成员函数是否存在
【发布时间】:2014-09-10 22:07:43
【问题描述】:

我想检查是否存在接受 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 中是否存在错误,或者代码在某些“实现定义”区域中,我不能假设它在所有平台上都会有相同的行为?

【问题讨论】:

  • 在专业化之前坚持依赖类型和std 类型。其他类型使用 ADL。那么铿锵会很高兴。我相信你所做的事情是错误的,但没有时间证明这一点
  • 看起来更像是 GCC 中的一个错误(至少 4.9.0),因为它打印 11 即使是 struct has_external_serialize&lt;T, void_t&lt;decltype(SomeNonExistingFunc(std::declval&lt;T&amp;&gt;()))&gt;&gt; : std::true_type
  • @Felics:欢迎来到 CppCon! :-)
  • 我无法回答你的问题,但这里有一个 g++ 也很满意的技巧:coliru.stacked-crooked.com/a/f7af49a7f53fbe4d
  • @Jamboree 谢谢!我知道还有其他方法可以解决这个问题,其中一些很好,就像你发布的那样,另一些带有一些恐怖代码:)

标签: c++ c++11 sfinae


【解决方案1】:

编译器之间的差异是由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;

both compilers produce 10.

§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 &amp;),并且对于 int&amp; 类型的参数没有 ADL,因此 10 是正确的输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-23
    相关资源
    最近更新 更多