Как специализировать классы для всех ссылочных типов С++ 03
Обратите внимание, что С++ 03 - это то, что мне действительно нужно, но ради знания, я хотел бы увидеть еще несколько хороших реализаций на С++ 11.
Мне нужен класс шаблона
template <typename T>
class A {
private:
T m_member;
public:
A(T _member);
//... MORE STUFF
void foo(T param);
};
Мне нужно:
1) Если A скомпилирован со значением типа (включая указатели, которые сами по себе передаются по значению):
Тогда мне нужно, чтобы A выглядело так (точно так же, как указано выше)
class A {
private:
T m_member;
public:
A(T _member);
//... MORE STUFF
void foo(T param);
};
2) Если A скомпилирован с эталонным типом (например, int&
):
Затем мне нужен A, чтобы выглядеть так:
class A{
private:
T& m_member;
public:
A(T& _member);
//... MORE STUFF
void foo(T param); // still the same T, not T&
};
Если бы я знал, что A получил только ints, тогда я мог бы использовать специализацию.
Но любой пользователь может использовать любой тип:
main.cpp
A<int> a1;//1st version
A<int&> a2;//2nd version
A<B> a3;//1st version
A<B&> a4;//2nd version
A<C*> a5;//1st version
Ответы
Ответ 1
Как видно (правильно) в этом потоке специализированный шаблон функции для ссылочных типов, предлагаемая здесь опция remove_reference не будет работать. Он просто не будет во второй реализации EVER, потому что компилятор видит T & и T точно так же.
Вместо этого вы могли бы MANUALLY сказать компилятору, что теперь он имеет дело с типом ссылки, используя тот же трюк специализации
template<typename T, bool isReference>
class A {
};
template<typename T>
class A<T,false>{
private:
T m_member;
public:
A(T _member);
//... MORE STUFF
void foo(T param);
}
/////////////////////////
template<typename T>
class A<T,true>{
private:
T& m_member;
public:
A(T& _member);
//... MORE STUFF
void foo(T param);
}
Если вы хотите извлечь какое-то подобное поведение и избежать дублирования кода, которое вызывает это решение, вы можете легко извлечь это поведение в Base Class<T>
и сделать
template<typename T,bool isReference>
class A : public BaseClass<T>{
}
и т.д.
Использование будет
main.cpp
A<int,false> a1;//1st version
A<int&,true> a2;//2nd version
A<B,false> a3;//1st version
A<B&,true> a4;//2nd version
A<C*,false> a5;//1st version, as pointers are value types
Ответ 2
Примечание: Кредит за идеи в этом ответе идет на n.m.
Чтобы ваша идея работала, вам просто нужно, чтобы тип аргумента функции для foo
всегда был не ссылочным типом. В С++ 11 для этого есть шаблон вспомогательной функции, называемый std::remove_reference
, но в С++ 03, который может быть легко реализован. Пример кода:
#include <iostream>
template<typename T> struct remove_reference {typedef T type;};
template<typename T> struct remove_reference<T&> {typedef T type;};
template<typename T>
class A{
private:
T m_member;
public:
A(T m): m_member(m) {}
void foo(typename remove_reference<T>::type param) { param = 0; }
};
struct B { int x; B(int x): x(x) {} };
struct C {};
int main()
{
int a = 1;
A<int> a1(a);
A<int&> a2(a);
a1.foo(a); std::cout << a << std::endl;
a2.foo(a); std::cout << a << std::endl;
B b(1);
A<B> b1(b);
A<B&> b2(b);
b1.foo(b); std::cout << b.x << std::endl;
b2.foo(b); std::cout << b.x << std::endl;
C c;
A<C*> c1(&c);
c1.foo(&c);
}
Вывод:
$ g++ -o r r.cc -std=c++03 && ./r
1
1
1
1
Как вы можете видеть, вывод 1
указывает, что аргумент передавался по значению (и поэтому param = 0
меняет локальную копию, а не объект, заданный как аргумент).
Еще один способ проверить, что код работает по назначению, - это вывести расширение gcc __PRETTY_FUNCTION__
внутри foo
. Выход для этой линии с вызовом a2.foo(a);
:
void A<T>::foo(typename remove_reference<T>::type) [with T = int&; typename remove_reference<T>::type = int]