Почему тип braced-init-list с одним элементом переключается на тип самого элемента?

В приведенном ниже коде вызов функции-члена F экземпляра b с аргументом {1,2} компилируется и вызывает B::F(std::initializer_list<int>), Но если я удаляю один элемент из списка braced-init-list и использую только {1}, я получаю ошибку

9 : error: no matching function for call to 'begin(int)' using type = decltype(std::begin(std::declval<T>()));

Я не понимаю, почему компилятор ищет begin(int), и не begin(initializer_list<int>)

Я играл с этим на https://godbolt.org/g/tMyYQs, и я получаю одинаковую ошибку как на Clang, так и на G ++. Что мне не хватает?

#include <type_traits>
#include <iterator>

template< bool B, class T = void >
using enable_if_t = typename std::enable_if<B,T>::type;

template <typename T>
struct mytrait {
  using type = decltype(std::begin(std::declval<T>()));
  };

template <typename T>
class A {
  public:
  template <typename TA, typename =
     enable_if_t<std::is_same<T, typename mytrait<TA>::type>::value>>
      A(TA &&){}
};

class B
{
  public:
    void F(A<int>);
    void F(std::initializer_list<int>);
};

int main()
{
  B b;

  b.F({1,2});    // compiles fine
#if 0
  b.F({1});      // causes mytrait<int>::type to be examined, 
                 // not mytrait<std::initializer_list<int>>::type
#endif 
}

1 ответ

Хорошо, я думаю, я понял это. Когда компилятор видит b.F({1}) он пытается выяснить, какую перегрузку F вызвать. Он видит, что есть перегрузка, которая принимает A<int>Таким образом, с помощью инициализации copy-list, он пытается увидеть, может ли он создать A<int> с помощью A<int>{1}, Тип буквального 1 это инт. Таким образом, ТА выводится как int. mytrait<int> пытается определить decltype(std::begin(declval<int>())), нет типа std::begin для типа int, поэтому ошибки компилятора.

За b.F({1,2})нет конструктора A<int> он принимает два ввода, поэтому инициализация списка даже не предпринимается.

Похоже, я могу это исправить, изменив объявление шаблона mytraits на

template <typename T, typename = decltype(std::begin(std::declval<T>()))>
struct mytraits {...};

Кажется, это использует SFINAE, чтобы mytrait:: type был ошибкой замещения.

Другие вопросы по тегам