[英]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.