【问题标题】:Clang 3.5: Detecting a gobal function doesn't exist using SFINAEClang 3.5:使用 SFINAE 检测 gobal 函数不存在
【发布时间】:2015-09-30 18:21:22
【问题描述】:

所以我有以下在 VS2012 下完美运行的代码。它检测是否存在全局函数“Bar”。

遗憾的是,在 clang 3.5 下编译失败并出现此错误:

error : use of undeclared identifier 'Bar'
  template <typename T> static int Foo( decltype( Bar )* ) { return 2; }            
                                                  ^ 

clang 是否支持这种事情,如果支持,正确的语法是什么?

谢谢

template <typename T> static int Foo( decltype( Bar )* ) { return 2; }      
template <typename T> static int Foo(...) { return 1; } 

void main()
{
    printf("%d", Foo<int>(nullptr));
}

【问题讨论】:

  • 不。那不应该编译
  • “全局”函数还是全局函数?
  • 这是一个 VS 错误。代码不合法​​。 SFINAE 中的 SF 代表替换失败。此代码在进行任何替换之前失败,即在模板定义处。
  • 那么如何使用 Clang 找出函数是否存在(注意。不是成员函数)?
  • @user176168 你可能对this question感兴趣。

标签: c++ clang sfinae


【解决方案1】:

如果您知道要查找的函数的确切签名,则可以使用 SFINAE 解决(使用 clang 3.6 和 gcc 4.9 测试):

#include <stdio.h>

int SomeMethod(double d) {
        // nop
        return 0;
}

struct HasSomeMethod {
        typedef char no[2];

        template <typename C>
        static auto test(C c) -> decltype(SomeMethod(c));
        //static auto test(C c) -> decltype(SomeOtherMethod(c));

        template <typename>
        static no& test(...);

        static const bool value = sizeof(test<double>(1.0)) == sizeof(int);
};

int main()
{
    printf("%d\n", HasSomeMethod::value);
}

如果将测试方法切换到注释版本,则会输出 1 和 0。

它适用于任何全局方法,只要它具有至少一个可以提取到模板参数的参数。

SFINAE 是“替代失败不是错误”的缩写。这意味着,您必须:

  • 使用将被编译器替换的模板参数
  • 您的主题必须以某种方式依赖此模板参数

我的示例通过向主题函数提供模板化参数来解决后者 - 如果没有这样的函数,则无法替换它,这是有效的。

如果不存在您正在寻找的此类重载,则结果相同。你可以在代码中更改 SomeMethod 的参数,或者在测试点更改,你会看到的。

不幸的是,对于这种情况,使用type_traits 和类似的技巧是不可能的。比如下面的方法:

template<typename C>
static auto test(C c) -> typename std::is_same<decltype(SomeXXMethod()), decltype(c)>::type

与您的原始代码有相同的问题。对于成员检测,这是可行的,因为C::SomeMethod 取决于模板参数。

由于这个要求,可能可以检查一个没有参数但返回类型为非 void 的方法,但我现在不知道如何检查。

但是如果你有一个没有参数的 void 方法,我相信你不会找到一个标准的方法来做 - 你找不到任何有效的类型规范,其中既有你的类型也有那个方法。

Clang 特定

如果你只是想要一个特定于 Clang 的检查,有 Clang specific extensions

例如,您可以使用#if __is_identifier(SomeMethod) 宏来检查给定标识符是否存在。它可能是一个变量,或者一个函数,或者任何东西,但是在这个块中,你可以使用 SFINAE 来决定它的类型。

【讨论】:

  • 谢谢 Dutow!如果函数签名是 void bar(void) 怎么办?看起来上面是使用 double 的模板参数来决定函数是否存在。
  • 对不起,由于某种原因完全错过了你上面所说的!谢谢
  • 我编辑了我的答案——你找不到检查 void(void) 方法的标准兼容方法——但是如果你只需要 clang ,有一个 clang 扩展可以帮助你。
  • 感谢 Dutow 的精彩回答。我不能再要求了。碰巧这个扩展适用于我的特殊情况,我不希望我的 UE4 Slate UI 包装器的用户不得不费力地为 UI 元素编写所有可选的委托。相反,我检测是否已经给出了一个函数,如果没有,则返回一个空委托。无论如何,再次感谢您!
  • 我刚试过。 __is_identifier 似乎测试某物是否是有效标识符,而不是它是否存在,这在给定名称的情况下是有意义的。例如__is_identifier(myFunnyMethod) 将返回true,无论你声明与否。
猜你喜欢
  • 1970-01-01
  • 2011-03-23
  • 1970-01-01
  • 2013-09-05
  • 2016-06-26
  • 2012-05-29
  • 1970-01-01
  • 1970-01-01
  • 2013-02-20
相关资源
最近更新 更多