What SFINAE tricks can I safely use on all MSVC >= 2013? - c++

In Qt I had the brilliant (cough, cough) idea to start defining overloads of qHash (the hashing function used for QHash, one of Qt's associative containers) for Standard Library datatypes: std::basic_string, std::shared_ptr and the like.
There could be a shortcut for this process: instead of chasing "any Standard Library type that could be used as a key in QHash" and adding a qHash overload for it, I could just define qHash automatically if the type has a std::hash specialization for it (reasonably assuming that we don't want to do more than what the Standard Library does in this process).
That is, I could implement something like this using expression SFINAE:
template<typename T>
auto qHash(const T &t) -> decltype(std::hash<T>()(t))
{
return std::hash<T>()(t);
}
Unfortunately, although Qt demands a C++11 compiler, expression SFINAE is not allowed anywhere because MSVC does not fully support it (at the time of this writing: all MSVC versions, up to and including VS15 preview 5. Anyhow, Qt must support all the way back to 2013).
Hence, the question: is there a way to do the same, in a way that
does not use expression SFINAE
is guaranteed to work on all MSVC versions >= 2013?
I was thinking a plain good ol' C++98 SFINAE construction via enable_if and the like, but other SO answers (like this one) make me think that MSVC 2013 may miscompile that too, so the result becomes unacceptable again.

I do not think you need expression SFINAE for this, something along these lines should work.
template<typename T>
typename std::hash<T>::result_type qHash(const T &t)
{
return std::hash<T>()(t);
}
Or pretty much any approach that does SFINAE on hash::result_type. Unfortunately for you, hash::result_type is deprecated in C++17, but you can still #ifdef this code for MSVC 2013.

Related

Is the c++ code in standard library all valid c++?

Just out of curiosity, I looked at how std::is_pointer is implemented and saw stuff like this (one of multiple overloads):
template <class _Ty>
_INLINE_VAR constexpr bool is_pointer_v<_Ty*> = true;
which if I copy and paste into my code and compile says constexpr is not valid here. What is happening?
Is it possible to implement structs like std::is_pointer and std::is_reference by us? (Not that I would, just curious so please don't come at me)
MRE with msvc 2019:
template <class _Ty>
inline constexpr bool is_pointer_v<_Ty*> = true;
int main()
{
}
--
Error: error C7568: argument list missing
after assumed function template 'is_pointer_v'
To answer the question in the title:
No, standard library code does not need to adhere to the language rules for user code. Technically it doesn't even need to be implemented in C++, but could e.g. be directly integrated into the compiler.
However, practically the standard library code is always compiled by the compiler just as user code is, so it will not use any syntax constructs that the compiler would reject for user code. It will however use compiler-specific extensions and guarantees that user code should not generally rely on. And there are also some reservations made specifically to the standard library implementation in the standard.
For example, the snippet you are showing from the standard library implementation is not valid user code, because _Ty is a reserved identifier that may not be used by user code, because it starts with an underscore followed by an uppercase letter. Such identifiers are specifically reserved to the standard library implementation. For this reason alone most standard library code will not be valid user code.
You are using a partial template specialization here. You need a complete declaration of the template to get that code to compile, like this:
template <class _Ty>
inline constexpr bool is_pointer_v = false;
template <class _Ty>
inline constexpr bool is_pointer_v<_Ty*> = true;
See here for example code
To answer your questions, the STL implementation requires specific C++ primitives that the compiler implements to support the required API. You can't have a constexpr version of the STL if the compiler does not implement it.
It's not possible to implement the complete STL without some compiler specifics code and the operating system's primitives. This is different from system to system (you can't use the STL implementation of linux on windows for example). It relies on undefined behavior, and many optimization which are known to be right for that specific compiler.
For example, you can't implement type punning (i.e converting from float* to int* and dereferencing) without UB in C++ (see here and here)
You have to rely on memcpy, but that can't be implemented without UB code (that is well defined if you write the compiler, BTW), since memcpy is accessing the memory likely not in the initial type that it's declared.
Yet, you can always copy & paste the STL code from your system and it'll always build correctly on your compiler.

How to test if std::remove_cvref is defined in the standard library?

I don't see anything resembling feature test macro for it in here:
https://en.cppreference.com/w/cpp/utility/feature_test
Neither it does not seem to be mentioned in the original paper:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0550r2.pdf
Testing for __cplusplus is also not appropriate since C++20 is not yet published but this feature could already be supported.
Could feature-test macro support appear later during standardization or is this addition too small to become part of feature-test macro and thus people who want to use the standard version conditionally are bound to get back to old school manual compiler version checking?
Looking through [tab:cpp.predefined.ft], I don't see anything possibly related to it either. Presumably because this feature is deemed to minor and t is very easy to implement one yourself:
template <typename T>
struct remove_cvref :remove_cv<remove_reference_t<T>> {};
template <typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
If you are just trying to write portable code, it suffices to roll out your own version. If you are genuinely try to detect the availability of this feature, you may have to resort to the "old school manual compiler version checking", unfortunately.

Why doesn't Qt's qHash() have an overload for std::shared_ptr?

I just found out, to my surprise, that the following code does not compile out of the box in C++14 using Qt 5.4:
QSet<std::shared_ptr<SomeType>> var;
The problem is that there is no overload of the qHash() method for std::shared_ptr, or any other smart pointer as far as I can see:
http://doc.qt.io/qt-5/qhash.html#related-non-members
It seems natural to me to have the following overload (or something similar):
template <typename T>
uint qHash(const std::shared_ptr<T>& ptr, uint seed = 0)
{
return qHash(ptr.get(), seed);
}
but it does not exist. This cannot simply be something the Qt developers overlooked. Do I need to include a special header? What is the reason why this does not exist?
Speak of the devil, and he doth appear:
https://codereview.qt-project.org/113340
This cannot simply be something the Qt developers overlooked.
It's not an overlook, it's just that Qt doesn't have endless resources to add qHash overloads to any STL type which has an operator==.
This cannot simply be something the Qt developers overlooked.
It is, and it's not something that was overlooked - it simply is impossible to write all the code at once. One has to stop somewhere and do a release. Feel free to submit a code revision that will fix this :) Make sure, though, that the revision will build on all platforms, and with all supported compilers - some of them have libraries without std::shared_ptr!

ADL fails when there are lambda arguments?

quite some time ago i noticed that in Visual C++ 10 ADL fails when at least one of the arguments is a lambda.
std::vector<float> vec;
for_each(begin(vec), end(vec), [](float) {});
The above fails to compile on VC++10 and 11 (beta) (begin and end are found via ADL). When i convert the lambda function into a regular free function things work just as expected.
I've asked on Herb Sutters blog once and also read some posts on msdn connect and the usual answers were: this is a bug, we havent implemented the latest standard of the lambdas yet which - at that time - was quite understandable. Things haven't been in a baked form yet. On MS connect there have also been disturbing comments that this will not be resolved for the next release i.e. vc 11.
My question is, is this code expected to work under the C++11 standard? I cant quite figure that out. Do i really have to prefix my for_each and other algorithms with std:: when I'm using lambdas?
I somehow suspect that this behavior will not change after vc++11 release.
The standard doesn't guarantee what you'd want it to..
With the below in mind we can easily realize that there is nothing guaranteeing that ADL would work in cases similar to the example provided in your post.
std::begin (c)/std::end (c)
The functions are described in the standard as the below quotation:
template <class C> auto begin(C& c) -> decltype(c.begin());
template <class C> auto end(C& c) -> decltype(c.end());
Though the Container< ... >::iterator (which is the return-type of c.begin ()) is an implementation-defined type.
More about the matter can be read upon at 24.5.6 Range Access, and 23.3.6.1/2 class template vector (or any other template STL container).
[](){} - Lambda Expressions
A lambda is an implementation-defined type, there is nothing in the standard stating that the resulting object will be of a type which is under namespace std.
It can pretty much exists wherever it wants, as long as it confirms to the other rules set up by the standard.
Too Long; Didn't Read
The types of which std::begin/std::end/a lambda-expression yields are not guaranteed to be under namespace std, therefore ADL is not guaranteed to kick in.
That is perfectly valid code. Any bug-free compiler will be able to compile it. But since MSVC has bug and so is unable to search the function through ADL, then maybe you should not rely on ADL and instead qualify it with std:: helping the compiler to find the function.

Detect template presence at compilation time

GCC up to 4.5 doesn't have standard C++0x type trait template has_nothrow_move_constructor. I could use it in my package for optimization, but I don't want to rule out one of the common compilers and don't want to overload configuration with symbols like HAVE_STD_HAS_NOTHROW_MOVE_CONSTRUCTOR. Is it somehow possible to use that template if present and just fall back to copying if not present without using any predefined configuration symbols? I also don't want to depend on Boost, since my library is small and doesn't need Boost for any other reasons.
In pseudocode, I need something like:
template <typename type>
struct has_nothrow_move_constructor_robust
: public integral_constant <bool,
/* if possible */ has_nothrow_move_constructor <type>::value
/* otherwise */ false>
{ };
Since move constructors are only for C++0x anyway, I don't mind using other C++0x features for the above definition, if at all possible.
boost::variant has an implementation of has_nothrow_move for its own internal use - you could use that, although it's not as reliable as a proper compiler implementation would be. The source for it is here - I don't know how reliable it is, so YMMV.
Apart from that, you could test compiler version macros (__GNUC__ and __GNUC_MINOR__) to determine presence, and stub it out if not present. Unfortunately it seems has_nothrow_move_constructor isn't supported in any released version of G++ yet, so you'll have to wait a bit before you'll know the right version to use.