簡體   English   中英

用於檢測模板函數的 Sfinae 類型特征不適用於 std::forward

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

我最近嘗試創建一個 sfinae 類型特征來檢測一個類是否包含一個名為construct的特定模板靜態函數。

我帶來了這個實現:

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;

我認為那沒問題,而且確實如此。 我試圖用 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");

它適用於兩個編譯器。

但是,一旦我添加轉發引用,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");

這里是關於 coliru 的代碼片段: GCC , Clang

我的問題是:GCC 和 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");

我不確定為什么你的代碼在 clang++(或傳入 g++)中失敗。 但這里有一個更簡單的選擇。

#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 在返回類型推導的 decltype 中指定std::forward<T>時特別不爽。 如果我們刪除它,就沒有問題。 但是,我現在不確定代碼的正確性!!

在 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)...);
    }
};

如您所見,在這種情況下,我們不必采取額外的步驟來欺騙編譯器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM