Определение типа шаблона внутри конструктора копирования не работает

Немного предыстории: я пишу интеллектуальный указатель на основе политики (например, SmartPtr в библиотеке Loki), который может иметь деструктивную семантику копирования, например auto_ptr. Следовательно, он должен иметь конструктор копии шаблона, использующий неконстантную ссылку для изменения копируемого объекта.

Что я пытаюсь сделать, так это параметризовать константность аргумента, принимаемого конструктором копирования, с помощью некоторой переменной в политике, чтобы сделать ее константой, когда семантика деструктивного копирования нежелательна. Вот упрощенный код идеи, к которой я пришел, но, к сожалению, он не работает, потому что компилятор не может вывести аргументы шаблона. Есть ли другой метод, который можно использовать для достижения желаемого поведения?

template <typename T, bool isEnabled> struct AddConst {
    typedef T Type;
};

template <typename T> struct AddConst<T, true> {
    typedef const T Type;
};

struct MyCopyPolicy {
    static const bool kIsCopyArgConst = true;
};

template <typename T, class CopyPolicy> struct Foo {

    // A helper struct to achieve "template typedef".
    template <typename T2> struct CopyArg {
        typedef typename AddConst<Foo<T2, CopyPolicy>,
            CopyPolicy::kIsCopyArgConst>::Type Type;
    };

    Foo() {}
    // Template copy constructor. Doesn't work.
    template <typename T2> Foo(typename CopyArg<T2>::Type& rhs) {}
};

int main() {
    Foo<int, MyCopyPolicy> foo1;
    Foo<double, MyCopyPolicy> foo2 = foo1;  // error!
}

person lizarisk    schedule 16.03.2011    source источник
comment
У вас есть проблема с невыведенным контекстом, как здесь ‹stackoverflow.com/questions/1268504/›. Загвоздка в том, что компилятор не может определить T2 без создания экземпляра CopyArg для всех возможных типов и сравнения их вложенных типов.   -  person Bo Persson    schedule 17.03.2011


Ответы (1)


Возможно, что-то подобное может сработать для вас. Я использовал std::enable_if из C++0x. Вы можете так же легко использовать boost или свернуть свой собственный (это всего пара строк кода).

#include <type_traits>

struct MyCopyPolicy {
    static const bool kIsCopyArgConst = true;
};

template <typename T, class CopyPolicy> struct Foo {

    // A helper struct to achieve "template typedef".
    template <typename T2> struct CopyArg {
        typedef CopyPolicy Type;
    };

    Foo() {}

    template <typename T2>
        Foo(const T2& rhs,
                typename std::enable_if<CopyArg<T2>::Type::kIsCopyArgConst>::type* = 0)
            {}
    template <typename T2>
        Foo(T2& rhs,
               typename std::enable_if<!CopyArg<T2>::Type::kIsCopyArgConst>::type* = 0)
            {}
};

int main() {
    Foo<int, MyCopyPolicy> foo1;
    Foo<double, MyCopyPolicy> foo2 = foo1;  // ok!
}

Я не уверен, что это оптимально. Это был просто бросок от бедра, когда одна из целей находилась на минимальном расстоянии от того места, куда вы направлялись. Возможно, это то, куда вы хотите пойти, или, возможно, это просто направит вас в правильном направлении.

person Howard Hinnant    schedule 17.03.2011
comment
Спасибо за отличную подсказку! Хотя этот трюк выглядит немного грязным из-за дополнительной фиктивной переменной, созданной в стеке. Однако я не вижу никакой возможности избежать этого. - person lizarisk; 17.03.2011
comment
В C++0x вы сможете добавить ограничение в качестве аргумента шаблона по умолчанию, а не в качестве аргумента функции по умолчанию. Я предпочитаю аргумент шаблона по умолчанию стилистически. Но мой компилятор предупредил, что это функция С++ 0x. - person Howard Hinnant; 17.03.2011