为什么带有一个元素的braced-init-list类型会切换到元素本身的类型?

时间:2016-04-14 23:39:59

标签: c++ c++11 initializer-list typetraits list-initialization

在下面的代码中,对带有参数{1,2}的实例b的成员函数F的调用进行编译并调用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 个答案:

答案 0 :(得分:1)

好吧,我想我弄明白了。当编译器看到b.F({1})时,它会试图找出要调用的F的重载。它看到有一个带有A<int>的重载,因此,通过copy-list-initialization,它会尝试查看它是否可以使用A<int>构建A<int>{1}。文字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&lt; int&gt; :: type成为替换失败。