2016-11-05 8 views
2

Я сталкиваюсь с проблемой при использовании спецификатора noexcept на производных классах, точнее, когда родитель является абстрактным классом (имеет конструкторы protected).Использование noexcept в производных классах

Далее приведен пример того, как я объявляю свои классы.

  • С конструктором public в базовом классе: все в порядке.
  • Тот же код с protected и производным классом не более «nothrow movable».

Пропустить что-нибудь? Является std::is_nothrow_move_constructible правильными чертами для использования в объявлениях производного класса или я должен использовать что-то еще?

Спасибо за ваши ответы.

#include <cstdlib> 
#include <iostream> 

class BaseOk 
{ 
public: 
    BaseOk (BaseOk&& other) noexcept {} 
}; 

class BaseNok 
{ 
protected: 
    BaseNok (BaseNok&& other) noexcept {} 
}; 

class ChildOk : public BaseOk 
{ 
public: 
    ChildOk (ChildOk&& other) noexcept (std::is_nothrow_move_constructible <BaseOk>::value) 
     : BaseOk (std::move (other)) {} 
}; 

class ChildNok : public BaseNok 
{ 
public: 
    ChildNok (ChildNok&& other) noexcept (std::is_nothrow_move_constructible <BaseNok>::value) 
     : BaseNok (std::move (other)) {} 
}; 

int main() 
{ 
    std::cout << std::boolalpha; 
    std::cout << "Is BaseOk move constructible?   " << std::is_move_constructible <BaseOk>::value << '\n'; 
    std::cout << "Is ChildOk move constructible?   " << std::is_move_constructible <ChildOk>::value << '\n'; 

    std::cout << '\n'; 
    std::cout << "Is BaseOk nothrow move constructible? " << std::is_nothrow_move_constructible <BaseOk>::value << '\n'; 
    std::cout << "Is ChildOk nothrow move constructible? " << std::is_nothrow_move_constructible <ChildOk>::value << '\n'; 

    std::cout << '\n'; 
    std::cout << "Is BaseNok move constructible?   " << std::is_move_constructible <BaseNok>::value << '\n'; 
    std::cout << "Is ChildNok move constructible?   " << std::is_move_constructible <ChildNok>::value << '\n'; 

    std::cout << '\n'; 
    std::cout << "Is BaseNok nothrow move constructible? " << std::is_nothrow_move_constructible <BaseNok>::value << '\n'; 
    std::cout << "Is ChildNok nothrow move constructible? " << std::is_nothrow_move_constructible <ChildNok>::value << '\n'; 
    std::cout << std::endl; 

    return EXIT_SUCCESS; 
} 

Выход:

Is BaseOk move constructible?   true 
Is ChildOk move constructible?   true 

Is BaseOk nothrow move constructible? true 
Is ChildOk nothrow move constructible? true 

Is BaseNok move constructible?   false 
Is ChildNok move constructible?   true 

Is BaseNok nothrow move constructible? false 
Is ChildNok nothrow move constructible? false 

___ EDIT ____________________________________________________________

После поиска вокруг некоторое время, и в отношении к andswer из Олег Богданов, он, к сожалению, кажется, не представляется возможным объединить protected с использованием noexcept (is_nothrow_...).

Я писал абстрактные классы и объявлял конструкторы protected только для целей документирования. Теперь конструкторы вернулись к public, но я столкнулся с другой проблемой:

как абстрактный класс не может быть инстанциирован, std::is_nothrow_move_constructible<BaseClass> возвращает false и все производные классы не могут быть помечены как не выбрасывающие исключений, даже если они не являются.

смотрите пример ниже:

#include <cstdlib> 
#include <iostream> 

class Foo 
{ 
public: 
    Foo (Foo&& other) noexcept {} 
    virtual ~Foo() = 0; // Removing '= 0' makes both outputs print 'true'. 
}; 
Foo::~Foo() {} 

class Bar : public Foo 
{ 
public: 
    Bar (Bar&& other) noexcept (std::is_nothrow_move_constructible <Foo>::value) 
     : Foo (std::move (other)) {} 
}; 

int main() 
{ 
    std::cout << std::boolalpha; 
    std::cout << "Foo: " << std::is_nothrow_move_constructible <Foo>::value << '\n'; 
    std::cout << "Bar: " << std::is_nothrow_move_constructible <Bar>::value << '\n'; 

    return EXIT_SUCCESS; 
} 

Выход:

Foo: false 
Bar: false 

ответ

1

При оценке в trueis_move_constructible работает точно так же, как is_constructible, которые, в свою очередь, говорит, что

Т представляет собой объект или ссылочный тип и определение переменной T obj (std :: declval() ...); хорошо сформированные

Я думаю, что в вашем случае, определение BaseNok obj(...) не очень хорошо сформированные, потому что ни у CTOR общественности по умолчанию (его неявно удалены), ни любой другой доступный т е р (защищенный не), таким образом, он равен false.(Определение хорошо formeness само по себе является спорным, хотя)

ChildNok еще move_constructible потому, что вы сделали свой ход CTOR общественности, другие случаи Eval для false именно потому, что std::is_move_constructible <BaseNok>::value уже false


Edit: Что касается редактируемого вопроса, примечание раздел is_constructible упоминает

Во многих реализациях is_nothrow_constructible также проверяет, если деструктор бросает, потому что это эффективно noexcept (T (Arg))

Когда вы держите деструктор чисто виртуальным, это, вероятно, не проходит проверку.

Я лично не уверен, если это недосмотр или-дизайн признаков типа, некоторые вопросы рассматриваются в LWG issue 2116

Я не предлагаю масштабируемое решение здесь, но почему бы не отмечать свои производные классы безоговорочно noexcept for now, учитывая, что base is noexcept() тоже

+0

Спасибо за ваш ответ. Да, я понял это. Я предложил минималистичную часть кода, но я попробовал со всеми определенными конструкторами (в 'protected'), и результат точно такой же.Я реализую абстрактный класс, и я попытался создать свой конструктор «protected» для целей документации (теперь я не обязателен). К сожалению, кажется, что невозможно использовать их 'protected' при использовании' noexcept' и 'std :: is _...'. –

+0

Я переместил все мои конструкторы в 'public', но столкнулся с другой проблемой. Я отредактировал мой оригинальный пост ... –

+1

Отредактировал свой ответ с дополнительными –

0

После многих исследований по всему Интернету я обнаружил, что единственным «приемлемым» решением является реализация моих собственных свойств, имеющих дело только с noexcept -ness. Возможность создания объекта или нет игнорируется, так как это является причиной проблемы с абстрактными классами.

Вот что я реализовал в своей библиотеке. Пояснения приводятся рядом с образцом кода.
(Примечание: az:: имен и AZ_ префиксы идентифицировать материал, предоставленный моей библиотеке.)

#include <cstdlib> 
#include <iostream> 

// --- Default traits --- 

namespace az 
{ 
    template < typename CLASS > struct has_noexcept_default_constructor { static const bool value = false; }; 
    template < typename CLASS > struct has_noexcept_copy_constructor { static const bool value = false; }; 
    template < typename CLASS > struct has_noexcept_move_constructor { static const bool value = false; }; 
    template < typename CLASS > struct has_noexcept_copy_operator { static const bool value = false; }; 
    template < typename CLASS > struct has_noexcept_move_operator { static const bool value = false; }; 
} 

// --- Helper macros --- 

#define AZ_SET_NOEXCEPT_DEFAULT_CONSTRUCTOR(CLASS, VALUE) \ 
template <> struct az::has_noexcept_default_constructor < class CLASS > { static const bool value = (VALUE); } 

#define AZ_SET_NOEXCEPT_COPY_CONSTRUCTOR(CLASS, VALUE) \ 
template <> struct az::has_noexcept_copy_constructor < class CLASS > { static const bool value = (VALUE); } 

#define AZ_SET_NOEXCEPT_MOVE_CONSTRUCTOR(CLASS, VALUE) \ 
template <> struct az::has_noexcept_move_constructor < class CLASS > { static const bool value = (VALUE); } 

#define AZ_SET_NOEXCEPT_COPY_OPERATOR(CLASS, VALUE) \ 
template <> struct az::has_noexcept_copy_operator < class CLASS > { static const bool value = (VALUE); } 

#define AZ_SET_NOEXCEPT_MOVE_OPERATOR(CLASS, VALUE) \ 
template <> struct az::has_noexcept_move_operator < class CLASS > { static const bool value = (VALUE); } 

// --- Foo class --- 

AZ_SET_NOEXCEPT_DEFAULT_CONSTRUCTOR (Foo, true); 
AZ_SET_NOEXCEPT_MOVE_CONSTRUCTOR (Foo, true); 

class Foo 
{ 
public: 
    Foo() noexcept (az::has_noexcept_default_constructor <Foo>::value) {} 
    Foo (Foo&& other) noexcept (az::has_noexcept_move_constructor <Foo>::value) {} 
    virtual ~Foo() = 0; 
}; 
Foo::~Foo() {} 

// --- Bar class --- 

AZ_SET_NOEXCEPT_DEFAULT_CONSTRUCTOR (Bar, az::has_noexcept_default_constructor <Foo>::value); 

class Bar : public Foo 
{ 
public: 
    Bar() noexcept (az::has_noexcept_default_constructor <Bar>::value) {} 
    Bar (Bar&& other) noexcept (az::has_noexcept_move_constructor <Bar>::value) : Foo (std::move (other)) {} 
}; 

// --- Tests --- 

int main() 
{ 
    std::cout << std::boolalpha; 

    bool fooHasNedc = az::has_noexcept_default_constructor <Foo>::value; 
    bool fooHasNecc = az::has_noexcept_copy_constructor <Foo>::value; 
    bool fooHasNemc = az::has_noexcept_move_constructor <Foo>::value; 

    bool fooIsNtdc = std::is_nothrow_default_constructible <Foo>::value; 
    bool fooIsNtcc = std::is_nothrow_copy_constructible <Foo>::value; 
    bool fooIsNtmc = std::is_nothrow_move_constructible <Foo>::value; 

    std::cout << "Foo has noexcept def/copy/move constructors: " << fooHasNedc << " " << fooHasNecc << " " << fooHasNemc << '\n'; 
    std::cout << "Foo is nothrow def/copy/move constructible: " << fooIsNtdc << " " << fooIsNtcc << " " << fooIsNtmc << '\n'; 
    std::cout << std::endl; 

    bool barHasNedc = az::has_noexcept_default_constructor <Bar>::value; 
    bool barHasNecc = az::has_noexcept_copy_constructor <Bar>::value; 
    bool barHasNemc = az::has_noexcept_move_constructor <Bar>::value; 

    bool barIsNtdc = std::is_nothrow_default_constructible <Bar>::value; 
    bool barIsNtcc = std::is_nothrow_copy_constructible <Bar>::value; 
    bool barIsNtmc = std::is_nothrow_move_constructible <Bar>::value; 

    std::cout << "Bar has noexcept def/copy/move constructors: " << barHasNedc << " " << barHasNecc << " " << barHasNemc << '\n'; 
    std::cout << "Bar is nothrow def/copy/move constructible: " << barIsNtdc << " " << barIsNtcc << " " << barIsNtmc << '\n'; 
    std::cout << std::endl; 

    return EXIT_SUCCESS; 
} 

Выход:

Foo has noexcept def/copy/move constructors: true false true 
Foo is nothrow def/copy/move constructible: false false false 

Bar has noexcept def/copy/move constructors: true false false 
Bar is nothrow def/copy/move constructible: true false false 

по умолчанию черты обеспечивают реализацию по умолчанию для метания Конструкторы & операторов присваивания.

Вспомогательные макросы делают реализацию специализированных признаков очень простыми. Они используются только один раз в файле заголовка. Затем признак используется как в файлах .hpp, так и .cpp. Таким образом, изменение значения noexcept в признаке (через макрос) обновляет как декларацию, так и определение (облегчение поддержки).

Как вы можете видеть, спецификатор noexcept конструктора по умолчанию Foo больше не скрывает его неконструктивный аспект.

Этот код отлично компилируется под VisualStudio 2015 и clang ++.
г ++ генерирует следующее сообщение об ошибке (я уверен, что он может быть установлен в том или иной форме ^^):

main.cpp:19:24: error: specialization of 'template<class CLASS> struct az::has_noexcept_default_constructor' in different namespace [-fpermissive] 
template <> struct az::has_noexcept_default_constructor < class CLASS > { static const bool value = (VALUE); } 
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

Надеется, что это может помочь людям, с которыми сталкиваются тем же вопрос. :)