I wrote simple check for operator[], but has_subscript_op struct template instantiation chooses wrong overload:
#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;
}
I'm using GCC 6.2.0
Coliru
Is this GCC bug, general bug, or I made an obvious mistake somewhere?
Just drop the & and use declval for the key too:
template<class T, class S>
struct has_subscript_op<T, S, std::void_t<decltype(std::declval<T>()[std::declval<S>()])>> : std::true_type {};
Live example at coliru
Why did the check with S() gave the wrong result? Because in GCC, it's considered as it was 0. A std::string can be constructed with a pointer, and 0 happens to be a null pointer constant.
Other compilers should not treat S() as it was 0 in C++14.
You can try for yourself:
std::map<std::string, std::string> test;
// compile fine, segfault at runtime
auto a = test[0];
// compile error!
auto b = test[2]
The check works better with std::declval because it's not a 0, neither a 2 but a plain int. Bonus, with declval, your check won't require the key to be default constructible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With