Предотвратить компилятор от рассмотрения неявно объявленного конструктора копирования в С++ 03

Обратите внимание, что я работаю в С++ 03, а функции delete d из С++ 11 недоступны мне.

Я пытаюсь создать объект, не подлежащий копированию, и не позволять компилятору рассматривать неявно объявленный конструктор копирования для этого класса. Это для устройства unit test, которое я разрабатываю.

Учтите, что у меня есть два основных объекта: объект основной библиотеки, Root и производный объект специального случая под тестом Branch. Я пытаюсь разработать класс test txt, Fixture, который обрабатывает мелочи настройки и общения с основным объектом Root. Итак, это упрощенная иллюстрация того, что я построил до сих пор:

(Вот идеонная ссылка с тем же кодом ниже, за исключением того, что я определил свой собственный noncopyable)

#include <boost/utility.hpp>
#include <boost/noncopyable.hpp>

class Root
{
};

class Fixture
:
    public boost::noncopyable
{
public:
    Fixture (Root& root)
    :
        mRoot (root)
    {
    }
private:
    Root& mRoot;
};

class Branch
:
    public Root,
    public Fixture
{
public:
    Branch()
    :
        Fixture (*this)
    {
    }
};

int main()
{
    Branch branch;
}

Компиляция результатов:

main.cpp: In constructor ‘Branch::Branch()’:
main.cpp:30:23: error: call of overloaded ‘Fixture(Branch&)’ is ambiguous
main.cpp:30:23: note: candidates are:
main.cpp:13:5: note: Fixture::Fixture(Root&)
main.cpp:8:7: note: Fixture::Fixture(const Fixture&)

Невозможно * предотвратить компилятор С++ 03 от неявного объявления конструктора копии для Fixture, если я не объявлю хотя бы один сам по себе. Но даже с:

class Fixture
:
    public boost::noncopyable
{
public:
    Fixture (Root& root)
    :
        mRoot (root)
    {
    }
private:
    Fixture (const Fixture&);
    Fixture (Fixture&);
    Root& mRoot;
};

... компилятор все равно рассмотрит эти объявления private при инициализации Fixture в Branch списке инициализации:

Fixture (*this)

Я хочу, чтобы компилятор просто не рассматривал эти конструкторы копирования.

Я мог бы сделать это, сделав небольшое искажение самостоятельно:

Fixture (static_cast <Root&> (*this))

... но я бы предпочел не так, поскольку это немного вонючий к носу и способность не копировать - это семантика того, что я собираюсь, выведя Fixture из boost::noncopyable.

Есть ли способ предотвратить компилятор от рассмотрения неявно объявленных конструкторов копирования в этом случае без изменения кода на сайте вызова из:

Fixture (*this)

?


  • "Это невозможно...": Standard С++ 03: 12.8/4, "Специальные функции-члены":

Если определение класса явно не объявляет копию конструктор, один объявлен неявно.

Ответ 1

Ваша двусмысленность заключается в том, что *this может связываться как с Root &, так и с Fixture &, и оба преобразования одинаково хороши (а именно преобразования с производной базой).

Хитрость заключается в создании перегрузки, которая лучше соответствует. Например,

template <typename T> Fixture(T &)

будет точно соответствовать любому lvalue и, следовательно, лучше соответствует перегрузке, требующей преобразования.

Однако, это слишком наивно, так как вы действительно не хотите, чтобы ваш Fixture был конструктивным из любой вещи и застенчивости. Скорее вы хотите, чтобы он был конструктивным только из того, что было получено из Root. Мы можем отключить посторонние конструкторы с помощью некоторой магии SFINAE. Сначала версия С++ 11:

#include <type_traits>

template <typename T,
          typename = typename std::enable_if<std::is_base_of<Root, T>::value>::type>
Fixture(T & x)
: mRoot(x)
{ }

В С++ 03 мы используем Boost, и мы не можем использовать аргументы шаблона по умолчанию:

#include <boost/type_traits.hpp>

template <typename T>
Fixture(T & x,
        typename boost::enable_if<boost::is_base_of<Root, T> >::type * = NULL)
: mRoot(x)
{ }

Теперь вам гарантируется, что T получен из Root. Перегрузка этого шаблонного конструктора с T = Branch является точным совпадением, лучше, чем конструктор копирования, и поэтому он выбирается как лучшая перегрузка однозначно.

Ответ 2

Невозможно предотвратить существование сигнатуры конструктора копирования, а не в С++ 98, а не в С++ 11. = delete не удаляет что-либо из набора перегрузки, он только терпит неудачу, если он выбран.

У меня нет лучшей идеи, чем вставить явный приведение, если вы не хотите вмешиваться в открытый интерфейс Fixture.

Параметры, которые взаимодействуют с интерфейсом, включают передачу указателя Root указателем, чтобы отличить от ссылки конструктор копирования и передачи тега ради разрешения перегрузки. Оставьте комментарий, если вы хотите узнать об этом подробнее.

Ответ 3

Если вы не собираетесь проходить Branch в качестве экземпляра Fixture, нет необходимости наследовать его вообще. То, что вы в основном хотели бы сделать, это установить что-то в Fixture для всех экземпляров Root, если я не ошибаюсь. Так что давайте атакуем эту причину, вместо сгибания С++. ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Если это в противном случае, у меня нет предложений, я боюсь.

Для этой проблемы я сделал бы Branch иметь экземпляр Fixture как член и перегрузить конструктор-копию Branch, чтобы создать экземпляр Fixture, передав себя как экземпляр в Fixture конструктор и оператор присваивания, чтобы никогда не копировать экземпляр Fixture. Ниже приведен краткий пример:

#include <boost/utility.hpp>
#include <boost/noncopyable.hpp>

class Root
{
};

class Fixture
:
    public boost::noncopyable
{
public:
    Fixture (Root& root)
    :
        mRoot (root)
    {
    }
private:
    Root& mRoot;
};

class Branch
:
    public Root
{
public:
    Branch()
    : mFixture(*this)
    {
    }

    Branch(const Branch& branch)
    : Root(*this)
    , mFixture(*this)
    /* other 'Branch' members to be copied */
    {
    }

    Branch& operator = (const Branch& branch)
    {
        Root::operator=(branch);
        /* copy other 'Branch' members, except 'mFixture' */
    }

    Fixture& getFixture()
    {
        return mFixture;
    }

    const Fixture& getFixture() const
    {
        return mFixture;
    }

private:
    Fixture mFixture;
};