为什么只有一个元素的支撑init列表的类型会切换到元素本身的类型

Why does type of braced-init-list with one element get switched to the type of the element itself?

本文关键字:元素 类型 init 有一个 为什么 列表      更新时间:2023-10-16

在下面的代码中,使用参数{1,2}对实例b的成员函数F的调用编译并调用B::F(std::initializer_list<int>)。但是,如果我从支持的init列表中删除一个元素并只使用{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 
}

好吧,我想我想通了。当编译器看到b.F({1})时,它试图找出要调用F的哪个重载。它发现有一个重载需要A<int>,因此,通过复制列表初始化,它试图查看是否可以使用A<int>{1}构造A<int>。文本1的类型是int。因此TA被推导为int。mytrait<int>试图确定decltype(std::begin(declval<int>())),类型int没有std::begin,因此编译器出错。

对于b.F({1,2})A<int>没有接受两个输入的构造函数,因此甚至没有尝试列表初始化。

看起来我可以通过将mytraits模板声明更改为来解决这个问题

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

这似乎是在利用SFINAE制作mytrait<int>::type是替换失败。