SFINAE - Обнаружить конструктор с одним аргументом

Кто-нибудь знает, как обнаружить конструктор с одним аргументом? Например, эта структура должна иметь отрицательный результат:

struct MyStruct
{
  MyStruct( int x, int x2 ) : y( x ) {}
  int y;
};

У меня есть хорошая проверка SFINAE, чтобы увидеть, является ли класс или структура конструктором с определенным количеством аргументов. Вот тот, для количества аргументов 3:

template <typename T>
struct HasCtor3Args
{
  struct Any { template <typename U> operator U( void ); };

  template <typename U>
  static int32 SFINAE( decltype( U( Any( ), Any( ), Any( ) ) ) * );

  template <typename U>
  static int8 SFINAE( ... );

  static const bool value = sizeof( SFINAE<T>( NULL ) ) == sizeof( int32 );
};

Кажется, это работает просто отлично, так как структура Any может преобразовываться в любые типы, которыми должны быть параметры. Однако проблема заключается в попытке обнаружить конструктор только с одним аргументом. Проверка SFINAE, кажется, всегда возвращает true из-за того, что по умолчанию Any имеет тот же тип, что и T, таким образом обнаруживая конструктор копирования.

Изменить и обновить. Я предпринял несколько попыток, но ни одна из них не увенчалась успехом... Это был самый близкий вариант, который я мог получить, но он не работает, поскольку всегда возвращает значение true. Идея заключалась в том, чтобы попытаться разрешить конструктор копирования вместо первого вызова «поймать все»:

template <typename T>
struct HasCtor1Args
{
  struct Any
  {
    template <typename U>
    operator U( ) const;
  };

  template <typename U>
  static int32 SFINAE( decltype( U( Any( ) ) ) * );

  // Try to catch the copy ctor here
  T MakeT( void );
  template <typename U>
  static int8 SFINAE( decltype( U( MakeT( ) ) ) * );

  template <typename U>
  static int8 SFINAE( ... );

  static const bool value = sizeof( SFINAE<T>( NULL ) ) == sizeof( int32 );
};

Я также пытался использовать явное ключевое слово вместе с функцией = delete для С++ 11, а затем понял, что компилятор, который мне нужно использовать (Microsoft), не позволяет этого. Я также пытался использовать std::enable_if для типа преобразования U, хотя столкнулся с ошибкой, что параметры шаблона функции не могут быть установлены по умолчанию.


person RandyGaul    schedule 21.04.2013    source источник
comment
О, и есть std::is_constructible   -  person dyp    schedule 22.04.2013
comment
По какой-то причине это работает, когда оператор преобразования является явным: template < typename U > explicit operator U() const;. Мне пришлось искать это в стандарте, но это может быть ошибка в g++ 4.8.   -  person dyp    schedule 22.04.2013
comment
Хм, я не думаю, что такое явное использование будет работать на компиляторе Microsoft.   -  person RandyGaul    schedule 22.04.2013
comment
Если есть два конструктора с одним аргументом, подстановка завершится ошибкой. Я не думаю, что есть способ обойти это.   -  person n. 1.8e9-where's-my-share m.    schedule 22.04.2013
comment
Комментарий n.m. также верен для многопараметрических ctors. Насколько мне известно, проблема заключается в неоднозначном преобразовании Any в тип параметра T, например. struct my_type{ my_type(int,int,int); my_type(int,int,bool); };   -  person dyp    schedule 22.04.2013
comment
Что такое конструктор с одним аргументом? Вы имеете в виду с одним параметром или вызываемым с одним аргументом или их комбинацией?   -  person Johannes Schaub - litb    schedule 22.04.2013
comment
Принимает только один параметр. Меня не волнует обработка случая нескольких перегрузок одного параметра, но я просто хочу определить, существует ли конструктор, принимающий один аргумент, не rvalue и не копируемый.   -  person RandyGaul    schedule 22.04.2013
comment
аргумент: ‹выражение вызова функции› выражение в списке, разделенном запятыми, заключенном в круглые скобки, параметр: объект или ссылка, объявленная как часть объявления или определения функции (N3485)   -  person dyp    schedule 22.04.2013
comment
Тогда параметр @DyP :)   -  person RandyGaul    schedule 22.04.2013
comment
Ну, я бы интерпретировал это как принимает ровно один аргумент или, что то же самое, имеет ровно один параметр;)   -  person dyp    schedule 22.04.2013


Ответы (1)


Хотя критика n.m. все еще остается в силе, вот версия для обнаружения типа с только одним 1-параметрическим некопируемым, неперемещаемым ctor. Он использует SFINAE для ограничения преобразования Any.

Примечание. Дополнительные ctors с аргументами по умолчанию могут привести к двусмысленности (например, my_type(int, double=0);. Это очень ограниченное решение.

#include <cstdint>
#include <type_traits>

template <typename T>
struct HasCtor1Args
{
    struct Any
    {
      template
      <
        typename U, typename SFINAE =
          typename std::enable_if< false == std::is_same<U,T>::value, U >::type
      >
      operator U() const;
    };

    template <typename U>
    static int32_t SFINAE( decltype( U( Any( ) ) ) * );

    template <typename U>
    static int8_t SFINAE( ... );

    static const bool value = sizeof( SFINAE<T>( nullptr ) ) == sizeof( int32_t );
};


struct my_type
{
    my_type(int);
    my_type(my_type const&);
};

int main()
{
    static_assert(HasCtor1Args<my_type> :: value, "epic fail");
}
person dyp    schedule 22.04.2013
comment
Ох, подождите, нвм... тип параметра будет просто выводиться как Any, я думаю. То же самое касается аргументов по умолчанию. Однако это не удастся для template<class T> C(Something<T>). - person Xeo; 22.04.2013
comment
Я не понимаю, почему это не удастся для этого. T просто становится типом Any. - person Johannes Schaub - litb; 22.04.2013
comment
@Johannes: Да, смотрите мой новый комментарий выше. - person Xeo; 22.04.2013
comment
.... но у нас была такая же неправильная мысль, Xeo :D тем не менее, проблема с аргументом по умолчанию сохраняется, я думаю. - person dyp; 22.04.2013
comment
Вы можете перегрузить тестовую функцию функцией, тип параметра которой определенно принимает любой тип (но не совсем любой тип, поэтому требуется определенное пользователем преобразование). Если теперь вызов заканчивается двусмысленностью (обнаружьте это с помощью sfinae), то T имеет один или несколько принимающих одноаргументных векторов). Это довольно хорошо справится с двусмысленностью (без каламбура). - person Johannes Schaub - litb; 22.04.2013
comment
Ах, я забыл, что функции преобразования не разрешено выполнять два пользовательских преобразования подряд, и что ваш существующий тест работает, потому что он выполняет приведение в самой тестовой функции. Для моего подхода преобразование должно выполняться парой аргумент->параметр, чтобы получить неоднозначную последовательность преобразования, определяемую пользователем. Очень жаль :-) - person Johannes Schaub - litb; 22.04.2013
comment
@JohannesSchaub-litb Мне было интересно, о чем вы говорили: D Однако это не мой подход, я просто изменил подход ОП. - person dyp; 22.04.2013
comment
@JohannesSchaub-litb Является ли неоднозначность преобразования ошибкой, которая вызывает использование менее жизнеспособной перегрузки в SFINAE? - person dyp; 22.04.2013
comment
Пытаюсь сделать это на компиляторе Microsoft, и кажется, что он просто не компилируется вместо выполнения SFINAE, когда Any не может преобразовать в тип T. Я предполагаю ошибку компилятора, если это работает на g++. Хотя это работает как проверка во время компиляции, мне на самом деле нужно, чтобы SFINAE выполнялась, так как дело в том, чтобы обрабатывать ложный случай во время компиляции. - person RandyGaul; 22.04.2013
comment
@RandyGaul Да, это работает на g++ 4.8; какую версию MSVC вы используете? - person dyp; 22.04.2013
comment
@DyP Компилятор Microsoft Visual C++, ноябрь 2012 г., CTP (v120_CTP_Nov2012) — кажется, единственный, который я смог найти, который поддерживает аргументы шаблона по умолчанию. Может быть, есть небольшая вариация, которая действительно будет работать на этом компиляторе? Я пытался угадать, но еще не получил рабочего решения. - person RandyGaul; 22.04.2013