简体   繁体   中英

Weird constructor SFINAE error with std::initializer_list

I can't understand why the following code doesn't compile. The error message given by the compiler isn't that helpful either.

Working example:

#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,
              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, I const end, size_t const x) {}
};

int
main() {

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

    return 0;
}

Clang's error message:

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, IteratorValue<I>>, 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, I const end, 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", "b", "c" }, 10);

The only int is the argument x but I can't see how's that affecting the code. Visual Studio gives me the same error too.

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, I const end, size_t const x) {}

Both of these functions will not compile if the template is instantiated.

Since your main function attempts to construct a Test from 2 parameters, adding a default value to the third parameter simply means that this function should be considered. It allows the template to be instantiated.

I still can't understand why the following code doesn't compile.

You are defining IteratorValue<I> (and therefore I::type ) before checking if I is an iterator.

Use the already defined std::iterator_traits to solve this.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM