문제 설명
멤버 변수에 std::enable_if 또는 유사한 메서드 사용 (Using std::enable_if or similar method on member variable)
현재 다음과 같은 사용자 정의 벡터 클래스가 있습니다.
template<int D, typename T = float>
class Vec
{
private:
T data[D];
public:
Vec(T initial = 0)
{
for (int i = 0; i < D; ++i)
{
data[i] = initial;
}
}
// Misc. operator overloads
};
using Vec2 = Vec<2>;
using Vec3 = Vec<3>;
using Vec4 = Vec<4>;
몇 가지 멤버 변수, 즉 x
, y
를 추가하고 싶습니다. z
및 w
는 각각 벡터의 첫 번째, 두 번째, 세 번째 및 네 번째 위치를 가리킵니다. 이상적으로는 벡터가 충분한 차원으로 선언된 경우에만 이러한 변수가 표시됩니다. 즉, 2D 벡터는 Vec::z
에 액세스할 수 없습니다.
제가 확인한 결과:
template<int D2 = D, typename T2 = typename std::enable_if<(D2 > 0), T*>::type>
T2 x(){ return &data[0];}
template<int D2 = D, typename T2 = typename std::enable_if<(D2 > 1), T*>::type>
T2 y(){ return &data[1];}
template<int D2 = D, typename T2 = typename std::enable_if<(D2 > 2), T*>::type>
T2 z(){ return &data[2];}
template<int D2 = D, typename T2 = typename std::enable_if<(D2 > 3), T*>::type>
T2 w(){ return &data[3];}
당신처럼 알 수 있듯이, 템플릿 매개변수 D
및 T
를 있는 그대로 입력하려고 하면 C++에 약간의 중년 위기가 오기 때문에 본질적으로 각각을 재정의해야 합니다. 그들을. 그리고 제가 말하려는 것이 터무니없지만 x
, y
, z
및 에 대해 정말 말하고 싶습니다. 내 의견으로는
vec.x
가 vec.x()
보다 더 멋지게 보이기 때문입니다. >
변수에는 템플릿이 없고 클래스와 함수만 있기 때문에 이 솔루션을 사용할 수 없습니다. 제가 현재 가지고 있는 것은 다음과 같습니다.
typename std::conditional<(D > 0), T*, std::nullptr_t>::type x = &data[0];
typename std::conditional<(D > 1), T*, std::nullptr_t>::type y = &data[1];
typename std::conditional<(D > 2), T*, std::nullptr_t>::type z = &data[2];
typename std::conditional<(D > 3), T*, std::nullptr_t>::type w = &data[3];
플로트, 정수 등의 유형을 원했기 때문에 폴백 캐스트로 std::nullptr_t
를 사용하기로 결정했습니다. 에 캐스팅할 수 없었습니다. 이것이 좋지 않다는 것을 알게 되었기 때문입니다. x
, y
, <이것은 여전히 4차원 미만의 class Vec
인스턴스에 대해 컴파일 오류를 발생시킵니다. 지금은 정말 혼란스럽습니다. 내가 얻는 오류는 cannot convert 'float*' to 'std::conditional::type' {aka 'std::nullptr_t}
의미가 없으면 getW()
를 실행해서는 안 되며, float*
와 std::nullptr_t
를 비교해서는 안 됩니다. ..?
참조 솔루션
방법 1:
You need to specialize Vec
to add these variables. Specializing the entire Vec
class would result in a whole lot of duplicated code so you can hoist it into a base class responsible for storing the data of a vector.
template<size_t D, typename T>
class VecStorage {
T data[D];
public:
T &operator[](const size_t i) {
return const_cast<T &>(std::as_const(*this)[i]);
}
const T &operator[](const size_t i) const {
assert(i < D);
return data[i];
}
};
template<typename T>
class VecStorage<2, T> {
public:
T x, y;
T &operator[](const size_t i) {
return const_cast<T &>(std::as_const(*this)[i]);
}
const T &operator[](const size_t i) const {
assert(i < 2);
if (i == 0) {
return x;
} else {
return y;
}
}
};
I've specialized VecStorage
for 2 dimensions. You can add specializations for 3 and 4 dimensions. Since VecStorage
exposes the subscript operator, Vec
and users of Vec
can treat it like an array.
template<size_t D, typename T = float>
class Vec : public VecStorage<D, T> {
public:
explicit Vec(T initial = 0) {
for (size_t i = 0; i != D; ++i) {
(*this)[i] = initial;
}
}
};
int main() {
Vec<2> v{42};
std::cout << v.x << ' ' << v.y << '\n'; // 42 42
v[0] = 5;
v[1] = 7;
std::cout << v.x << ' ' << v.y << '\n'; // 5 7
}
(by 小奥利奥、Indiana Kernick)
참조 문서