Ошибка замены не является вопросом об ошибке (SFINAE) с static_cast - PullRequest
2 голосов
/ 27 июля 2011

Дано:

class Hokey
{
public:
    explicit C(int i): i_(i) { }

    template<typename T>
    T& render(T& t) { t = static_cast<T>(i_); return t; }
private:
    unsigned i_;
};

Если я попытаюсь:

Hokey h(1);
string s;
h.render(s);

Кодовая панель выдает ошибку при статическом приведении:

t.cpp: In member function 'T& Hokey::render(T&) [with T = std::string]':
t.cpp:21:   instantiated from here
Line 11: error: no matching function for call to 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(unsigned int&)'

Похоже, следует сказать, что нет Hokey::render для сопоставления.

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

string& render(string& s) const {
    ostringstream out;
    out << i_;
//  s = out.str();
    return s;
}

Разве SFINAE не говорит о том, что - в первом случае проблема в рендеринге не должна быть ошибкой, а отсутствие работающего рендера должно быть ошибкой?

1 Ответ

7 голосов
/ 27 июля 2011

Это не ошибка при разрешении перегрузки. Другими словами, это откладывает выдачу вам ошибки, пока он не будет уверен, что вызов определенно не будет работать. После этого это ошибка.

struct example
{
    template <typename T>
    static void pass_test(typename T::inner_type); // A

    template <typename T>
    static void pass_test(T); // B

    template <typename T>
    static void fail_test(typename T::inner_type); // C
};

int main()
{
    // enumerates all the possible functions to call: A and B
    // tries A, fails with error; error withheld to try others
    // tries B, works without error; previous error ignored
    example::pass_test(5);

    // enumerates all the possible functions to call: C
    // tries C, fails with error; error withheld to try others
    // no other functions to try, call failed: emit error
    example::fail_test(5);
}

Следует также отметить, что разрешение перегрузки (и, следовательно, SFINAE) смотрит только на сигнатуру функции, , а не на определение . Так что это всегда будет неудачей:

struct example_two
{
    template <typename T>
    static int fail_test(T x)
    {
        return static_cast<int>(x);
    }

    template <typename T>
    static int fail_test(T x)
    {
        return boost::lexical_cast<int>(x);
    }
};

int main()
{
    example_two::fail_test("string");
}

Нет ошибок ни для замены шаблона, ни для сигнатур функций, поэтому обе функции можно вызывать, даже если мы знаем, что первая не удастся, а вторая - нет. Так что это дает вам неоднозначную ошибку вызова функции.

Вы можете явно включить или отключить функции с помощью boost::enable_if (или std::enable_if в C ++ 0x, что эквивалентно boost::enable_if_c). Например, вы можете исправить предыдущий пример с помощью:

struct example_two_fixed
{
    template <typename T>
    static boost::enable_if<boost::is_convertible<T, int>, int>
        pass_test(T x) // AA
    {
        return static_cast<int>(x);
    }

    template <typename T>
    static boost::disable_if<boost::is_convertible<T, int>, int>
        pass_test(T x) // BB
    {
        return boost::lexical_cast<float>(x);
    }
};

struct empty {} no_conversion;

int main()
{
    // okay, BB fails with SFINAE error because of disable_if, does AA
    example_two::pass_test(5);

    // okay, AA fails with SFINAE error because of enable_if, does BB
    example_two::pass_test("string");

    // error, AA fails with SFINAE, does BB, fails because cannot lexical_cast
    example_two::pass_test(no_conversion);
}
...