Использование C ++ 11 std :: enable_if для включения функции-члена, если вектор имеет определенную длину

Я пишу простой векторный класс, и я хотел бы иметь некоторые функции-члены, которые доступны только в векторах определенной длины (например, перекрестное произведение для 3-элементного вектора). Я наткнулся на std :: enable_if и похоже, что он может делать то, что я хочу, но я не могу заставить его работать правильно.

#include <iostream>
#include <type_traits>

template<typename T, unsigned int L>
class Vector
{
private:
T data[L];

public:
Vector<T,L>(void)
{
for(unsigned int i = 0; i < L; i++)
{
data[i] = 0;
}
}

T operator()(const unsigned int i) const
{
return data[i];
}

T& operator()(const unsigned int i)
{
return data[i];
}

Vector<typename std::enable_if<L==3, T>::type, L> cross(const Vector<T,L>& vec2) const
{
Vector<T,L> result;

result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1);
result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2);
result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0);

return result;
}
};

int main(void)
{
Vector<double,3> v1;
Vector<double,3> v2;
Vector<double,3> v3;
//Vector<double,4> v4;

v1(0) = 1;
v1(1) = 2;
v1(2) = 3;

v2(0) = 4;
v2(1) = 5;
v2(2) = 6;

v3 = v1.cross(v2);

std::cout << v3(0) << std::endl;
std::cout << v3(1) << std::endl;
std::cout << v3(2) << std::endl;

return 0;
}

Приведенный выше код компилируется и работает правильно, однако, если я раскомментирую объявление Vector<double,4> v4 Я получаю следующую ошибку при компиляции:

vec.cpp: In instantiation of ‘class Vector<double, 4u>’:
vec.cpp:46:22:   required from here
vec.cpp:29:59: error: no type named ‘type’ in ‘struct std::enable_if<false, double>’

Кто-то может указать, где я иду не так?

8

Решение

 template<unsigned LL = L>
Vector<typename std::enable_if<LL==3 && L == 3, T>::type, LL>
cross(const Vector<T,LL>& vec2) const
{
Vector<T,L> result;

result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1);
result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2);
result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0);

return result;
}

PS. Почему это работает так?

Определение переменной v4 вызывает неявную реализацию шаблона класса Vectorчто, в свою очередь, вызывает, помимо прочего, неявную реализацию объявлений функций-членов класса (14.7.1. Неявная реализация [temp.inst] # 1). Это последнее создание, конечно, приводит к ошибке.

Если вместо этого мы изменим функцию-член на шаблон-член, согласно тому же предложению, на этом этапе сам шаблон участника создается, и эта реализация выглядит более или менее, как:

template<unsigned LL = 3>
Vector<typename std::enable_if<LL==3 && 3 == 3, double>::type, LL>
cross(const Vector<double,LL>& vec2) const;

который является полностью действительным объявлением шаблона. На данный момент мы не выполняем (и не можем) выполнять дальнейшую реализацию.

Тем не менее, когда мы пытаемся на самом деле позвонить crossэто, без сомнения, «контекст, который требует определения члена / функции», поэтому, согласно (14.7.1 неявной реализации [temp.inst] # 2, # 3), cross шаблон (второй cross шаблон, тот, который является результатом создания экземпляра шаблона внешнего класса) неявно создается и std::enable_if предоставляется возможность сделать свою работу. В качестве дополнительного примечания, это ситуация, в которой применим принцип SFINAE.

PPS. Более подробно, хотя это и не связано напрямую с вопросом OP, но все же стоит упомянуть, что не всегда необходимо объявлять членов в качестве шаблонов для обработки подобных ситуаций.

Существуют ситуации, когда член шаблона класса не является «допустимым» для данного экземпляра, но все же шаблон класса может быть создан, например:

#include <type_traits>

template<typename T>
struct S
{
T x;

T foo () const { return x; }

typename std::remove_pointer<T>::type bar () const { return *x; }
};

S<int> x;
S<int *> y;

Видимо, в экземпляре S<int>, выражение *x неверно, потому что тип x является int, Эта программа верна, хотя.
Важным моментом является то, что во время неявной реализации только декларации из членов создаются. В приведенном выше случае, экземпляр S<int> вызывает декларация int bar() const; быть созданным, что является полностью правильной декларацией.

Конечно, если мы позже попытаемся создать экземпляр определение из S<int>::bar, как в:

void f()
{
x.foo ();
//  x.bar (); // error
y.foo ();
y.bar ();
}

мы получим ошибку.

(Это все еще следует из вышеупомянутых двух параграфов стандарта C ++)

8

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]