Проблемы с enable_if SFINAE

У меня возникли необъяснимые проблемы с SFINAE в программе, которую я пишу, поэтому я свел ее к отдельному примеру программы:

#include <type_traits>

struct Base { };

struct Derived : public Base { };

template<typename T>
struct From { };

template<typename T>
struct To {
    template<typename U>
    To(const From<U>& other) {
        static_assert(std::is_convertible<U*, T*>::value, "error");
    }
};

int main() {
    From<Derived> a;

    To<Base> b = a;
}

Эта программа компилируется без ошибок или предупреждений. Однако это:

#include <type_traits>

struct Base { };

struct Derived : public Base { };

template<typename T>
struct From { };

template<typename T>
struct To {
    template<typename U>
    To(const From<typename std::enable_if<true, U>::type>& other) {
    // this       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        static_assert(std::is_convertible<U*, T*>::value, "error");
    }
};

int main() {
    From<Derived> a;

    To<Base> b = a;
}

Выдает следующую ошибку:

test.cpp: в функции int main():

test.cpp: 22: 18: ошибка: преобразование из From<Base> к нескалярному типу To<Derived> запрошенный

Я полагаю, что замена не удалась, а конструктор не виден.

Я делаю SFINAE неправильно, или это ошибка компилятора? Я использую GCC 4.7.1 Rubenvb на Windows (с std=c++11 если это имеет значение).

1 ответ

Решение

Я бы использовал аргумент шаблона по умолчанию, таким образом аргумент может быть выведен:

#include <type_traits>

struct Base { };

struct Derived : public Base { };

template<typename T>
struct From { };

template<typename T>
struct To {
    template<typename U, class = typename std::enable_if<true, U>::type>
    To(const From<U>& other) {
    // this       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        static_assert(std::is_convertible<U*, T*>::value, "error");
    }
};

int main() {
    From<Base> a;

    To<Derived> b = a;
}

Обратите внимание, что это вызывает static_assert потерпеть неудачу, так как вы используете std::is_convertible наоборот. Так должно быть:

static_assert(std::is_convertible<T*, U*>::value, "error");

В вашем примере тип шаблона U не может быть выведено. В моем коде это может быть выведено, так как он используется в качестве аргумента шаблона для other аргумент в конструкторе. В вашем коде компилятор видит std::enable_if<true, U>::type и не могу понять, что это U Тип есть. Тот факт, что результатом этого enable_if используется в качестве аргумента шаблона для From не помогает вообще, так как U нужно выводить до этого.

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