简体   繁体   English

用于检测模板函数的 Sfinae 类型特征不适用于 std::forward

[英]Sfinae type trait to detect template function doesn't work with std::forward

I recently tried to make a sfinae type trait to detect if a class contain a particular template static function named construct .我最近尝试创建一个 sfinae 类型特征来检测一个类是否包含一个名为construct的特定模板静态函数。

I came with this implementation:我带来了这个实现:

template<typename T, typename... Args>
struct has_template_construct_helper {
private:
    template<typename U, typename... As>
    static std::true_type test(decltype(&U::template construct<As...>)*);

    template<typename...>
    static std::false_type test(...);

public:
    using type = decltype(test<T, Args...>(nullptr));
};

template<typename T, typename... Args>
using has_template_construct = typename has_template_construct_helper<T, Args...>::type;

I tought that would be okay, and it was.我认为那没问题,而且确实如此。 I tried to test my trait with gcc and clang like this:我试图用 gcc 和 clang 来测试我的特征,如下所示:

struct TestStruct {
    template<typename... Args>
    static auto construct(int a, double b, Args... args) -> decltype(std::make_tuple(a, b, args...)) {
        return std::make_tuple(1, 2.3, std::forward<Args>(args)...);
    }
};

// didn't fire! Hurrah!
static_assert(has_template_construct<TestStruct, std::string>::value, "Don't pass the test");

It worked for both compiler.它适用于两个编译器。

However, as soon as I add forwarding references, clang starts complaining:但是,一旦我添加转发引用,clang 就开始抱怨:

struct TestStruct {
    template<typename... Args>
    static auto construct(int a, double b, Args&&... args) -> decltype(std::make_tuple(a, b, std::forward<Args>(args)...))
    {
        return std::make_tuple(1, 2.3, std::forward<Args>(args)...);
    }
};

// fires on clang :(
static_assert(has_template_construct<TestStruct, std::string>::value, "Don't pass the test");

Here the code snippet on coliru: GCC , Clang这里是关于 coliru 的代码片段: GCC , Clang

My question is: which one between GCC and Clang is wrong, and how can I fix my code to make it work on both compiler?我的问题是:GCC 和 Clang 之间哪一个是错误的,我该如何修复我的代码以使其在两个编译器上都能正常工作?


Okay, I tried things, now I'm even more confused.好吧,我尝试了一些东西,现在我更困惑了。 When using std::declval , it worked back in clang!使用std::declval ,它会在std::declval恢复工作!

struct TestStruct {
    template<typename... Args>
    static auto construct(int a, double b, Args&&... args) -> decltype(std::make_tuple(a, b, std::declval<Args>()...))
    {
        return std::make_tuple(1, 2.3, std::forward<Args>(args)...);
    }
};

// uh?? Works in clang?
static_assert(has_template_construct<TestStruct, std::string>::value, "Don't pass the test");

I am not exactly sure why your code is failing in clang++ (or passing in g++).我不确定为什么你的代码在 clang++(或传入 g++)中失败。 But here is an easier alternative.但这里有一个更简单的选择。

#include <type_traits>
#include <tuple>
#include <string>

template <typename... T>
using void_t = void;

class Stat {
public:
    template <typename... T>
    static auto construct(int a, double b, T&&... t) ->
      decltype(std::make_tuple(1, 2.3, t...))
    {
      return std::make_tuple(1, 2.3, std::forward<T>(t)...);
    }
};

template <typename Class, typename... Args>
constexpr auto does_have_construct(int)
    -> decltype(&Class::template construct<Args...>, true)
{
    return true;
}

template <typename Class, typename... Args>
constexpr bool does_have_construct(long) { return false; }

class Stat2 {};

int main() {
    static_assert(does_have_construct<Stat, std::string>(0), "Nope!");

    return 0;
}

Clang is particularly unhappy when specifying std::forward<T> in the decltype of return type deduction. Clang 在返回类型推导的 decltype 中指定std::forward<T>时特别不爽。 If we remove that, there is no issue.如果我们删除它,就没有问题。 BUT , I am not sure now about the correctness of the code!!但是,我现在不确定代码的正确性!!

In C++14 you could rewrite the class Stat as:在 C++14 中,您可以将class Stat重写为:

class Stat {
public:
    template <typename... T>
    static auto construct(int a, double b, T&&... t)
    {
      return std::make_tuple(1, 2.3, std::forward<T>(t)...);
    }
};

As you can see, we do not have to take the extra step to fool the compiler in this case.如您所见,在这种情况下,我们不必采取额外的步骤来欺骗编译器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM