I recently tried to make a sfinae type trait to detect if a class contain a particular template static function named 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:
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:
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
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?
Okay, I tried things, now I'm even more confused. When using std::declval
, it worked back in clang!
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++). 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. 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:
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.