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

Есть ли способ проверить, имеет ли класс С++ конструктор по умолчанию (кроме атрибутов типа, предоставляемого компилятором)?

Классы классов могут быть определены для проверки того, имеет ли класс С++ переменную-член, функцию или тип (см. here).

Любопытно, что ConceptTraits не содержат признаков, чтобы проверить, определяет ли класс С++ конструктор по умолчанию или заданный конструктор?

Можно ли использовать черты для проверки присутствия конструктора? Если да, то как? Если нет, то почему это невозможно?

4b9b3361

Ответ 1

К сожалению, для ответа может возникнуть вопрос.

Googling Я обнаружил, что фактическая причина, по которой мы не можем проверить, имеет ли класс конструктор или деструкторы, известный метод, используемый для определения того, имеет ли класс член, основан на принятии адреса члена. Но конструкторы и деструкторы не имеют имени, мы не можем взять их адрес.

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

Итак, чтобы ответить на мой собственный вопрос, я бы сказал, что с помощью существующих методов их невозможно обнаружить, а поддержка компилятора необходима. Но С++ обнаружил много сюрпризов, и все, что было невозможно в определенный момент времени, было обнаружено, используя другую технику.

Я надеюсь, что эксперт по языку С++ читает это и может дать более четкое объяснение.

Ответ 2

Концептуальные черты больше не поддерживаются, но становятся частью Типовых признаков. И в документах has_trivial_constructor и has_trivial_destructor, Boost авторы ясно объясняют, что поддержка компилятора необходима для выполнения этой работы.

Ответ 3

Предупреждение: приведенный ниже анализ устарел с С++ 11. В С++ 11 проверка доступа выполняется до создания экземпляра, а нарушение прав доступа - это не ошибка. Поэтому прилагаемый код может быть более совместимым. Я еще не проанализировал его.


Я новичок в SFINAE. Сегодня мне пришло в голову ввести тестовое выражение внутри sizeof внутри параметра шаблона в виде аргумента функции.

В соответствии с N2634 это не так, но очень неуправляемо. ( EDIT:, похоже, совместим с С++ 0x FCD.) Он может возвращать положительный результат или не компилироваться в GCC 4.2; GCC 4.5 оценивает 3 из 3 для моих тестовых случаев.

Правила SFINAE были расширены (в данном случае) с С++ 03 в FCD. Новый §14.8.2/8 (выделено мной):

Если подстановка приводит к недопустимому типу или выражению, вычет типа не выполняется. Недопустимый тип или выражение - это тот, который был бы плохо сформирован, если он написан с использованием замещенных аргументов. Проверка доступа не выполняется как часть процесса подстановки. Следовательно, при успешном завершении дедукции ошибка доступа может возникать при создании экземпляра функции. Только недопустимые типы и выражения в непосредственном контексте типа функции и ее типов параметров шаблона могут привести к сбою дедукции. [Примечание. Оценка замещенных типов и выражений может приводить к возникновению побочных эффектов, таких как создание специализированных специализированных шаблонов классов и/или функциональных шаблонов, генерация неявно определенных функций и т.д. Такие побочные эффекты не находятся в "непосредственном контекст" и может привести к плохой форме программы.

template< class T >
class is_default_constructible {
    template<int x>
    class receive_size{};

    template< class U >
    static int sfinae( receive_size< sizeof U() > * );

    template< class U >
    static char sfinae( ... );

public:
    enum { value = sizeof( sfinae<T>(0) ) == sizeof(int) };
};

class q { q(); };
class r { r(int); };

#include <iostream>
using namespace std;

int main() {
    cerr << is_default_constructible<int>::value << endl // outputs 1
        // fails to compile: access violation
        // FCD demands that access violations be unrecoverable
        // indeed, it murky: q is default-constructible, but only "rarely"
        //<< is_default_constructible<q>::value << endl
        << is_default_constructible<r>::value << endl; // outputs 0
}

Ответ 4

Модификация ответа Potatoswatter

Работает на gcc-4.6

#include <type_traits>

template< class T >
struct identity {
typedef T type;
};

template<bool, typename T, typename F>
struct if_c : identity<F> {};

template< typename T, typename F>
struct if_c<true,T,F> : identity<T> {};

template<typename Bool, typename T, typename F>
struct if_ : if_c< Bool::value, T, F> {};

template< class T >
struct is_default_constructible_;

template< class T >
struct is_default_constructible :
  if_< std::is_arithmetic<T>,
    std::true_type,
    is_default_constructible_<T> >::type { };


template< class T >
struct is_default_constructible_ {

    template<class D> class Acessible : public D
    {
      friend class is_default_constructible_<D>;
      public:
      //using D::D; may be needed once N2540 is implemented 
    };

    template<int x>
    class receive_size{};

    template< class U >
    static int sfinae( receive_size< sizeof Acessible<U>() > * );

    template< class U >
    static char sfinae( ... );

public:
    enum { value = sizeof( sfinae<T>(0) ) == sizeof(int) };

};

struct p { p(); };
class q { q(); };
class r { r(int); };

#include <iostream>
using namespace std;

int main() {
    cerr << is_default_constructible<int>::value << endl // outputs 1
        << is_default_constructible<p>::value << endl
        << is_default_constructible<q>::value << endl
        << is_default_constructible<r>::value << endl; // outputs 0
}

# g++ - mp-4.6 --std = С++ 0x -Wall test.cpp && & &./a.out
1
1
0
0

Ответ 6

Возможно, вы захотите проверить этот пример кода, взятый из libstdС++ в Gcc 4.6.1, и который я слегка изменил для работы с MSVC 2010:

/! \: is_default_constructible возвращает true, даже если конструктор по умолчанию является закрытым или защищенным, но я до сих пор не могу найти способ решить эту проблему?):

namespace std {

namespace detail {

template<typename _B1, typename _B2>
struct __and_
  : public conditional<_B1::value, _B2, _B1>::type
{ };

template<typename _Pp>
struct __not_
  : public integral_constant<bool, !_Pp::value>
{ };


template<typename _Tp>
struct __is_array_known_bounds
  : public integral_constant<bool, (extent<_Tp>::value > 0)>
{ };

template<typename _Tp>
struct __is_array_unknown_bounds
  : public __and_<is_array<_Tp>, __not_<extent<_Tp>>>::type
{ };

struct __do_is_default_constructible_impl
{

  template<typename _Tp>
  static true_type __test(int,decltype(_Tp())* a = 0);

  template<typename>
  static false_type __test(...);
};

template<typename _Tp>
  struct __is_default_constructible_impl
  : public __do_is_default_constructible_impl
  {
    typedef decltype(__test<_Tp>(0)) type;
  };

template<typename _Tp>
  struct __is_default_constructible_atom
  : public __and_<__not_<is_void<_Tp>>,
                  __is_default_constructible_impl<_Tp>>::type
  { };

template<typename _Tp, bool = is_array<_Tp>::value>
  struct __is_default_constructible_safe;

// The following technique is a workaround for a current core language
// restriction, which does not allow for array types to occur in 
// functional casts of the form T().  Complete arrays can be default-
// constructed, if the element type is default-constructible, but 
// arrays with unknown bounds are not.
template<typename _Tp>
  struct __is_default_constructible_safe<_Tp, true>
  : public __and_<__is_array_known_bounds<_Tp>,
          __is_default_constructible_atom<typename
                    remove_all_extents<_Tp>::type>>::type
  { };

template<typename _Tp>
  struct __is_default_constructible_safe<_Tp, false>
  : public __is_default_constructible_atom<_Tp>::type
  { };
} // namespace detail
/// is_default_constructible
template<typename _Tp>
  struct is_default_constructible
: public integral_constant<bool, (detail::__is_default_constructible_safe<
                _Tp>::value)>
{ };

}

использовать:

class DefaultConstructible
{
public:
  DefaultConstructible() {}
};

class NotDefaultConstructible
{
public:
  NotDefaultConstructible(int i) {}
};

std::is_default_constructible<DefaultConstructible>::value // -> true
std::is_default_constructible<NotDefaultConstructible>::value // -> false

Ответ 7

После того, как я пролил много крови, пота и слез, я наконец нашел способ, который работает на каждом компиляторе, который я пробовал:

template<class T = void> struct is_default_constructible;

template<> struct is_default_constructible<void>
{
protected:
    // Put base typedefs here to avoid pollution
    struct twoc { char a, b; };
    template<bool> struct test { typedef char type; };
public:
    static bool const value = false;
};
template<> struct is_default_constructible<>::test<true> { typedef twoc type; };

template<class T> struct is_default_constructible : is_default_constructible<>
{
private:
    template<class U> static typename test<!!sizeof(::new U())>::type sfinae(U*);
    template<class U> static char sfinae(...);
public:
    static bool const value = sizeof(sfinae<T>(0)) > 1;
};