Подтвердить что ты не робот

Как специализировать классы для всех ссылочных типов С++ 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
4b9b3361

Ответ 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]