带有 std::initializer_list 的奇怪构造函数 SFINAE 错误

问题描述

我不明白为什么下面的代码不能编译。编译器给出的错误消息也没有那么有用。

工作示例:

#include <string>
#include <type_traits>

template <typename T>
struct TIteratorValue {
    using Type = typename T::Type;
};

template <typename T>
struct TIteratorValue<T *> {
    using Type = T;
};

template <typename T>
using IteratorValue = typename TIteratorValue<T>::Type;

template <typename T>
struct Test {
    template <typename V,std::enable_if_t<std::is_constructible_v<T,V>,int> = 0>
    Test(std::initializer_list<V> const list,size_t const x = 0)
        : Test(list.begin(),list.end(),x) {}

    template <typename I,IteratorValue<I>>,int> = 0>
    // Does not compile!
    Test(I begin,I const end,size_t const x = 0) {}
    // Compiles!
    //Test(I begin,size_t const x) {}
};

int
main() {

    Test<std::string> test({ "a","b","c" },10);

    return 0;
}

Clang 的错误信息:

C:\Users\joaom\DropBox\++A\so\weird_overloading.cpp:6:24: error: type 'int' cannot be used prior to '::' because it has no members     
        using Type = typename T::Type;
                              ^
C:\Users\joaom\DropBox\++A\so\weird_overloading.cpp:15:1: note: in instantiation of template class 'TIteratorValue<int>' requested here
using IteratorValue = typename TIteratorValue<T>::Type;
^
C:\Users\joaom\DropBox\++A\so\weird_overloading.cpp:25:50: note: in instantiation of template type alias 'IteratorValue' requested here
                          std::enable_if_t<std::is_constructible_v<T,int> = 0>
                                                                      ^
C:\Users\joaom\DropBox\++A\so\weird_overloading.cpp:27:2: note: while substituting prior template arguments into non-type template parameter
      [with I = int]
        Test(I begin,size_t const x = 0) {}
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\joaom\DropBox\++A\so\weird_overloading.cpp:35:20: note: while substituting deduced template arguments into function template 'Test'
      [with I = int,$1 = (no value)]
        Test<std::string> test({ "a",10);

唯一的 int 是参数 x,但我看不出它如何影响代码。 Visual Studio 也给了我同样的错误

解决方法

template <typename I,std::enable_if_t<std::is_constructible_v<T,IteratorValue<I>>,int> = 0>
// Does not compile!
Test(I begin,I const end,size_t const x = 0) {}
// Compiles!
//Test(I begin,size_t const x) {}

两个如果模板被实例化,这两个函数将不会编译

由于您的 main 函数试图从 2 个参数构造一个 Test,因此向第三个参数添加默认值仅意味着应该考虑该函数。它允许实例化模板。

我还是不明白为什么下面的代码不能编译。

您正在定义 IteratorValue<I>(因此是 I::type之前检查 I 是否是迭代器。

使用已经定义的 std::iterator_traits 来解决这个问题。

// Formatted for clarity
template <typename I,std::enable_if_t<
              std::is_constructible_v<
                  T,typename std::iterator_traits<I>::value_type
              >,int
          > = 0>