这就是我正在尝试做的事情:
template <typename T> struct Model
{
vector<T> vertices ;
#if T has a .normal member
void transform( Matrix m )
{
each vertex in vertices
{
vertex.pos = m * vertex.pos ;
vertex.normal = m * vertex.normal ;
}
}
#endif
#if T has NO .normal member
void transform( Matrix m )
{
each vertex in vertices
{
vertex.pos = m * vertex.pos ;
}
}
#endif
} ;
我看过使用enable_if
的
示例,但我不明白如何将
enable_if
应用于这个问题,或者是否可以应用。
使用 C++11,这变得更容易。
template <typename T> struct Model
{
vector<T> vertices;
void transform( Matrix m )
{
for(auto &&vertex : vertices)
{
vertex.pos = m * vertex.pos;
modifyNormal(vertex, m, special_());
}
}
private:
struct general_ {};
struct special_ : general_ {};
template<typename> struct int_ { typedef int type; };
template<typename Lhs, typename Rhs,
typename int_<decltype(Lhs::normal)>::type = 0>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, special_) {
lhs.normal = rhs * lhs.normal;
}
template<typename Lhs, typename Rhs>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, general_) {
// do nothing
}
};
注意事项:
decltype(Lhs::normal)
”部分更改为“decltype(&Lhs::myFunc)
”。decltype
和 sizeof
中命名非静态数据成员,而不需要对象。我知道这个问题已经有一些答案,但我认为我对这个问题的解决方案有点不同,可以帮助某人。
以下示例检查传递的类型是否包含
c_str()
函数成员:
template <typename, typename = void>
struct has_c_str : false_type {};
template <typename T>
struct has_c_str<T, void_t<decltype(&T::c_str)>> : std::is_same<char const*, decltype(declval<T>().c_str())>
{};
template <typename StringType,
typename std::enable_if<has_c_str<StringType>::value, StringType>::type* = nullptr>
bool setByString(StringType const& value) {
// use value.c_str()
}
如果需要检查传递的类型是否包含特定数据成员,可以使用以下方法:
template <typename, typename = void>
struct has_field : std::false_type {};
template <typename T>
struct has_field<T, std::void_t<decltype(T::field)>> : std::is_convertible<decltype(T::field), long>
{};
template <typename T,
typename std::enable_if<has_field<T>::value, T>::type* = nullptr>
void fun(T const& value) {
// use value.field ...
}
更新C++20
C++20 在此 C++ 版本中引入了约束和概念、核心语言功能。
如果我们想检查模板参数是否包含
c_str
成员函数,那么,以下将完成工作:
template<typename T>
concept HasCStr = requires(T t) { t.c_str(); };
template <HasCStr StringType>
void setByString(StringType const& value) {
// use value.c_str()
}
此外,如果我们想检查可转换为
long
的数据成员是否存在,可以使用以下方法:
template<typename T>
concept HasField = requires(T t) {
{ t.field } -> std::convertible_to<long>;
};
template <HasField T>
void fun(T const& value) {
// use value.field
}
通过使用 C++20,我们可以获得更短、更易读的代码,清楚地表达其功能。
#include <iostream>
#include <vector>
struct Foo {
size_t length() { return 5; }
};
struct Bar {
void length();
};
template <typename R, bool result = std::is_same<decltype(((R*)nullptr)->length()), size_t>::value>
constexpr bool hasLengthHelper(int) {
return result;
}
template <typename R>
constexpr bool hasLengthHelper(...) { return false; }
template <typename R>
constexpr bool hasLength() {
return hasLengthHelper<R>(0);
}
// function is only valid if `.length()` is present, with return type `size_t`
template <typename R>
typename std::enable_if<hasLength<R>(), size_t>::type lengthOf (R r) {
return r.length();
}
int main() {
std::cout <<
hasLength<Foo>() << "; " <<
hasLength<std::vector<int>>() << "; " <<
hasLength<Bar>() << ";" <<
lengthOf(Foo()) <<
std::endl;
// 1; 0; 0; 5
return 0;
}
相关https://ideone.com/utZqjk归功于 freenode IRC 上的 dyreshark #c++
。
使用enable_if和decltype让编译器检查变量,希望有帮助。
requires
关键字,但提供的代码对于您的需求来说仍然太复杂,需要为每种情况创建单独的函数。这是适合您的用例的更简单的代码,其中单个函数实现就足够了:
template <typename T> struct Model
{
vector<T> vertices ;
void transform( Matrix m )
{
each vertex in vertices
{
vertex.pos = m * vertex.pos ;
if constexpr (requires { &vertex.normal; })
vertex.normal = m * vertex.normal ;
}
}
} ;
备注:
if constexpr
线上。我按原样保留了伪代码,但删除了冗余并添加了
if constexpr
行。
requires
表达式只是尝试访问
normal
成员的地址,如果表达式无效,则计算结果为 false
。您确实可以使用任何表达式,如果定义了 normal
,则该表达式会成功;如果未定义,则失败。
normal
的类,请确保可以从此代码访问该成员(例如,它是
public
或指定了适当的友谊)。否则,代码将忽略 normal
成员,就好像它根本不存在一样。
typedef int Matrix;
struct NormalVertex {
int pos;
int normal;
};
struct Vertex {
int pos;
};
template <typename T> struct Model
{
typedef int No;
typedef char Yes;
template<typename U> static decltype (declval<U>().normal, Yes()) has_normal(U a);
static No has_normal(...);
vector<T> vertices ;
template <typename U = T>
typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(Yes), void>::type
transform( Matrix m )
{
std::cout << "has .normal" << std::endl;
for (auto vertex : vertices)
{
vertex.pos = m * vertex.pos ;
vertex.normal = m * vertex.normal ;
}
}
template <typename U = T>
typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(No), void>::type
transform( Matrix m )
{
std::cout << "has no .normal" << std::endl;
for (auto vertex : vertices)
{
vertex.pos = m * vertex.pos ;
}
}
} ;
int main()
{
Matrix matrix;
Model <NormalVertex> normal_model;
Vertex simple_vertex;
Model <Vertex> simple_model;
simple_model.transform(matrix);
normal_model.transform(matrix);
return 0;
}
#include <boost/tti/has_member_data.hpp>
BOOST_TTI_HAS_MEMBER_DATA(normal)
template <typename T> struct Model
{
vector<T> vertices;
static constexpr bool hasNormal = has_member_data_normal<T, double>::value;
template<bool B = hasNormal, std::enable_if_t<B, int> = 0>
void transform( Matrix m )
{
for(auto&& vertex : vertices)
{
vertex.pos = m * vertex.pos ;
vertex.normal = m * vertex.normal ;
}
}
template<bool B = hasNormal, std::enable_if_t<!B, int> = 0>
void transform( Matrix m )
{
for(auto&& vertex : vertices)
{
vertex.pos = m * vertex.pos ;
}
}
};
如果你不想依赖boost,那么你可以使用@ltjax的答案
来创建你自己的has_member_data_normal
结构。