SFINAE проверка для оператора [] более запутана, чем я?

Я написал простой чек для operator[], но has_subscript_op экземпляр шаблона структуры выбирает неправильную перегрузку:

#include <iostream>
#include <type_traits>
#include <string>
#include <map>

template<class, class, class = void>
struct has_subscript_op : std::false_type
{ };

template<class T, class S>
struct has_subscript_op<T, S, std::void_t<decltype(&std::declval<T>()[S()])>> : std::true_type
{ };

int main()
{
    //true, nice
    std::cout << "int[][int]: " << has_subscript_op<int[], int>::value << std::endl;
    //false, nice
    std::cout << "int[][float]: " << has_subscript_op<int[], float>::value << std::endl;
    //true, nice
    std::cout << "std::string[int]: " << has_subscript_op<std::string, int>::value << std::endl;
    //true, WAT?
    std::cout << "std::map<std::string, std::string>[int]: " << has_subscript_op<std::map<std::string, std::string>, int>::value << std::endl;
}

Я использую GCC 6.2.0

Coliru

Это ошибка GCC, общая ошибка, или я где-то допустил явную ошибку?

1 ответ

Решение

Просто бросьте & и использовать declval для ключа тоже:

template<class T, class S>
struct has_subscript_op<T, S, std::void_t<decltype(std::declval<T>()[std::declval<S>()])>> : std::true_type {};

Живой пример в колиру

Почему проверка с S() дал неправильный результат? Потому что в GCC считается, как это было 0, std::string может быть построен с указателем, и 0 бывает константой нулевого указателя.

Другие компиляторы не должны относиться S() как это было 0 в C++14.

Вы можете попробовать для себя:

std::map<std::string, std::string> test;

// compile fine, segfault at runtime
auto a = test[0];

// compile error!
auto b = test[2]

Чек лучше работает с std::declval потому что это не 0ни 2 но равнина int, Бонус, с declvalваш чек не потребует, чтобы ключ был конструируемым по умолчанию.

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