SFINAE 不在 llvm/clang 上工作

SFINAE not working on llvm/clang

本文关键字:工作 clang 不在 llvm SFINAE      更新时间:2023-10-16

在下面的代码中,我试图调用一个函子,它采用任何参数,"whatever"是一组有限的选项(这里的两个不是我代码中唯一的选项)。

#include <memory>
#include <iostream>
template<class T>
struct call_with_pointer {
    // last resort: T*
    template<class Callable>
    static auto call(Callable &callable, const std::shared_ptr<T> &param) -> decltype(callable(param.get())) {
        return callable(param.get());
    }
};
template<class T>
struct call_with_shared : public call_with_pointer<T> {
    // best: call with shared_ptr<T>.
    // SFINA
    // error: Candidate template ignored: substitution failure [with Callable = Test]: no matching function for call to object of type 'Test'
    template<class Callable>
    static auto call(Callable &callable, const std::shared_ptr<T> &param) -> decltype(callable(param)) {
        return callable(param);
    }
    using call_with_pointer<T>::call;
};

class Test {
public:
    bool operator () (int * x) {
        return *x == 42;
    }
};
int main ()
{
    Test t;
    auto i = std::make_shared<int>(4);
    auto x = call_with_shared<int>::call(t, i); // No matching function for call to 'call'
    return 0;
}

这段代码在 VS 和 GCC 中运行良好。不幸的是,它不会发出叮当声。错误消息是:

调用到"呼叫"没有匹配函数

忽略候选模板:替换失败 [可调用 = 测试]:没有匹配函数 调用类型为"测试"的对象

因此,它会忽略使用智能指针的候选项。好。但它似乎并没有继续考虑继承的调用,可以正常工作。

问题:我该如何解决此问题?我怎样才能让llvm在这里做正确的事情?

尝试以下操作:

template<class T>
struct call_with_pointer {
    // last resort: T*
    template<class Callable>
    static auto call(Callable &callable, const std::shared_ptr<T> &param) -> decltype(callable(param.get())) {
        return callable(param.get());
    }
};
template<class T>
struct call_with_pointer_2 {
    // last resort: T*
    template<class Callable>
    static auto call(Callable &callable, const std::shared_ptr<T> &param) -> decltype(callable(param)) {
        return callable(param);
    }
};
template<class T>
struct call_with_shared : public call_with_pointer<T>, public call_with_pointer_2<T>{
    using call_with_pointer<T>::call;
    using call_with_pointer_2<T>::call;
};

严格来说,clang 是正确的,因为 C++11 的 7.3.3p15(继承函数模板的 using 声明被忽略,因为它与派生类的成员函数模板具有相同的名称和参数)。尽管很明显,该段没有考虑这些不冲突的声明是有缺陷的。

您可以通过使用类似 typename YieldFirstType<std::shared_ptr<T>, Callable>::type 的内容作为其中一个模板中的第二个参数类型来解决此问题。