用于检测模板函数的 Sfinae 类型特征不适用于 std::forward
Sfinae type trait to detect template function doesn't work with std::forward
我最近尝试创建一个finae类型trait来检测一个类是否包含一个名为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
时,它可以在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");
我不太确定为什么你的代码在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)...);
}
};
如您所见,在这种情况下,我们不必采取额外的步骤来欺骗编译器。
相关文章:
- basic_string的前导/尾部不区分空格的特征
- 特征 LLT 模块给出不正确的结果?
- C++ 特征图3.5,特征图不使用命名返回值优化?
- 零四元数和任何向量都不为零的特征积,这是一个错误吗?
- 为什么我不能在同一 MS VS 解决方案中的两个控制台应用中使用C++特征(仅标头库)?
- 计算数组的特征值/向量,而不是使用特征 3 计算矩阵
- 特征获取索引数组,其中向量中的值为真(不需要循环)
- OpenGL/Glew C++纹理不适用
- 定义模板化结构的特征时出现不完整的类型错误
- 在不使用经过训练的模型的情况下检测/分割面部和面部特征:openCV C++
- 当我在 windows7 中安装程序时,我指定的字体大小不适用
- accelerator.cu(8): 错误:属性"managed"在这里不适用?
- 是否应该避免使用不支持特征的模块?
- typedef X=<T>T::UserType1,但如果不适用,typedef X<T>=UserType2
- SymEigsShiftSolver of Spectra是否不返回特征向量?
- 用C++编写一个程序,对一个简单的序列求和:1/N + 2/N-1 + 3/N-2+ ...不适用
- SFINAE在这里不适用吗?
- 覆盖和扩展数组或矩阵,而不在特征中定义其大小
- 当是复制和交换习语不适用
- 在函数实参中使用模板形参不适用gcc4.8