应用SFINAE检查是否为T定义了一个trait
Apply SFINAE to check if a trait is defined for T
我目前正在研究一个小的数学向量类。我想要两个向量类,Vector2
和Vector3
彼此是可构造的。例如:
Vector2<float> vec2(18.5f, 32.1f); // x = 18.5; y = 32.1
Vector3<float> vec3(vec2); // x = 18.5; y = 32.1; z = float()
为了做到这一点,为了便于扩展,我想使用一个特征VectorTraits
,其基本定义如下:
template <typename T>
struct VectorTraits
{
typedef T VectorType;
typedef typename T::ValueType ValueType;
static const unsigned int dimension = T::dimension;
};
这个表单允许用户在现有的Vectors类(比如glm::vec2)和我的类之间建立链接。这样就可以从一个glm::vec2创建一个Vector2。
此外,该技术可以允许我为使用SFINAE定义VectorTraits
的所有类编写通用流操作符。
这是我的问题,我没能定义operator<<
,所以当VectorTraits
不适合给定类型时,这是无声的错误。
这是我最后一次尝试:
#include <iostream>
#include <type_traits>
// To define another operator
struct Dummy
{};
// Traits class
template <typename T>
struct VectorTraits
{
typedef T VectorType;
typedef typename T::ValueType ValueType;
static const std::uint16_t dimension = T::dimension;
};
// Fake vector class. Defines the required typedef.
struct Vec
{
typedef float ValueType;
static const std::uint16_t dimension = 2;
};
// Streaming operator for Dummy.
std::ostream& operator<<(std::ostream& stream, const Dummy& d)
{
stream << "dummy.n";
return stream;
}
// Streaming operator attempt for classes defining VectorTraits.
template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>>
std::ostream& operator<<(std::ostream& stream, const T& vec)
{
std::cout << "Traits. Dimension = " << VectorTraits<T>::dimension << "n";
}
int main()
{
std::cout << "Testn";
std::cout << Vec();
std::cout << Dummy();
return 0;
}
在此尝试中,错误只是
error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'Vec')
prog.cpp:33:15: note: candidate: template<class T, typename std::enable_if<(VectorTraits<T>::dimension > 0), void>::type <anonymous> > std::ostream& operator<<(std::ostream&, const T&)
std::ostream& operator<<(std::ostream& stream, const T& vec)
^
prog.cpp:33:15: note: template argument deduction/substitution failed:
prog.cpp:41:19: note: couldn't deduce template parameter '<anonymous>'
如果我改变
template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>>
template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = 0>
我得到另一个错误
prog.cpp:13:35: error: 'char [21]' is not a class, struct, or union type
typedef typename T::ValueType ValueType;
我设法得到工作的唯一版本是一个空的VectorTraits
类,必须为每个Vector
专门化。但我也想提供一种方法来"自动"成为一个Vector
与一些typedef
的定义。
我不明白为什么在show版本中,编译器没有保留我的操作符。我也尝试了一些变体,但它总是要么匹配一切,要么不匹配。
一个问题是您没有为std::enable_if_t
实例化的结果提供默认参数,因此模板参数推导失败。解决这个问题的一种方法是添加* = nullptr
:
template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = nullptr>
std::ostream& operator<<(std::ostream& stream, const T& vec)
然而,现在我们得到一个错误,因为在VectorTraits<T>
实例化中,T::ValueType
是必需的。这不是在SFINAE上下文中,因此如果该成员不存在,将发生硬失败。我们可以通过在模板参数中添加SFINAE检查来解决这个问题:
template <class T, typename = typename T::ValueType,
std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = nullptr>
std::ostream& operator<<(std::ostream& stream, const T& vec)
您可以将其分解为外部IsValidVector
检查,以便如果您需要多次进行此类检查,您可以更新单个点。
你可以添加一个图层到你的vectorTrait,只有当它是有效的:
// Traits class
template <typename T, typename = void>
struct VectorTraits_impl
{
};
template <typename T>
struct VectorTraits_impl<T,
std::enable_if_t<std::is_integral<decltype(T::dimension)>::value>>
{
typedef T VectorType;
typedef typename T::ValueType ValueType;
static const std::uint16_t dimension = T::dimension;
};
// Traits class
template <typename T>
using VectorTraits = VectorTraits_impl<T>;
演示由于以下原因,您的特定代码无法工作。
对于矢量,std::enable_if_t<(VectorTraits<T>::dimension > 0)>
是某种类型。因此,可以将operator<<
声明为模板,其中第二个参数是该类型的值。对于Dummy
(和char*
)没有这样的类型,所以SFINAE排除了这一点。但是对于Vec
,您将std::enable_if_t<(VectorTraits<T>::dimension > 0)>
设置为某种类型,并且编译器期望模板参数是该类型的某些值。当然,它没有办法找出这个值应该是什么
Jarod42提供的解决方案在Visual Studio上不起作用,所以我设法找到了另一个。我把它贴出来是为了帮助那些有一天遇到这个问题的人:
首先,我声明了类VectorTraits
,但没有这样定义它:
template <typename T, typename = void>
struct VectorTraits;
,我提供了一个专门化,只有当T::dimension > 0
:
template <typename T>
struct VectorTraits<T, std::enable_if_t<(T::dimension > 0)>>
{
typedef T VectorType;
typedef typename T::ValueType ValueType;
static const std::uint16_t dimension = T::dimension;
};
最后,我通过使用std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>
:
template <class T, typename = std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>>
std::ostream& operator << (std::ostream& stream, const T& )
{
return stream << "Traits. Dimension = " << VectorTraits<T>::dimension << "n";
}
最后的工作示例可以在这里或下面找到:
#include <iostream>
#include <type_traits>
// Traits class
template <typename T, typename = void>
struct VectorTraits;
template <typename T>
struct VectorTraits<T, std::enable_if_t<(T::dimension > 0)>>
{
typedef T VectorType;
typedef typename T::ValueType ValueType;
static const std::uint16_t dimension = T::dimension;
};
// Fake vector class. Defines the required typedef.
struct Vec
{
typedef float ValueType;
static const std::uint16_t dimension = 2;
};
// Fake vector class. Defines the required typedef.
struct VecFake
{
};
template <>
struct VectorTraits<VecFake>
{
typedef VecFake VectorType;
typedef float ValueType;
static const std::uint16_t dimension = 12;
};
// Streaming operator attempt for classes defining VectorTraits.
template <class T, typename = std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>>
std::ostream& operator << (std::ostream& stream, const T& )
{
return stream << "Traits. Dimension = " << VectorTraits<T>::dimension << "n";
}
int main()
{
std::cout << "Testn"; // Test
std::cout << Vec(); // Traits. Dimension = 2
std::cout << VecFake(); // Traits. Dimension = 12
}
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 运行同一解决方案的另一个项目的项目
- 挂起和取消挂起一个文件DLL
- 用C++中的一个变量定义一个常量
- 函数向量_指针有不同的原型,我可以构建一个吗
- 在c++中用vector填充一个简单的动态数组
- 如何在选项卡视图Qt中设置一个新项目,并保存以前的项目
- 预处理器:插入结构名称中的前一个行号
- 我在c++代码中生成了一个运行时#3异常
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 从链接列表c++中删除一个项目
- 告诉一个 const char 数组,除了编译时 C 样式的字符串外,它不以 '