Есть ли способ проверить, имеет ли класс C ++ конструктор по умолчанию (кроме предоставленных компилятором черт типа)? - PullRequest
21 голосов
/ 29 апреля 2010

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

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

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

Ответы [ 7 ]

4 голосов
/ 30 апреля 2010

Извините за ответ на собственный вопрос.

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

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

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

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

2 голосов
/ 20 февраля 2011

Модификация ответа 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 = c ++ 0x -Wall test.cpp && ./a.out
1
1
0
0

2 голосов
/ 05 мая 2010

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


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

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

Правила SFINAE были расширены (в данном случае) начиная с C ++ 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's murky: q is default-constructible, but only "rarely"
        //<< is_default_constructible<q>::value << endl
        << is_default_constructible<r>::value << endl; // outputs 0
}
2 голосов
/ 01 мая 2010

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

0 голосов
/ 11 августа 2014

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

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;
};
0 голосов
/ 20 октября 2011

Вы можете проверить этот пример кода, взятый из libstdc ++ в 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
0 голосов
/ 30 апреля 2010

MSDN говорит, что заголовок определяет has_default_constructor и такие черты.

http://msdn.microsoft.com/en-us/library/bb982179.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...