Предотвратить компилятор от рассмотрения неявно объявленного конструктора копирования в С++ 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;
};