Warn against missing std:: prefixes due to ADL - c++

It is possible to omit the std:: namespace for <algorithm>s when the argument types are in that namespace, which is usually the case. Is there any warning or clang-tidy rule that finds such omissions?
#include <vector>
#include <algorithm>
std::vector<int> v;
for_each(v.begin(), v.end(), [](auto){});
return 0;
The above example, compiled with the latest clang and -Wall, -Wextra and -Wpedantic does not emit any diagnostic:
https://godbolt.org/z/dTsKbbEKe

There is an open change in tidy that could be used to flag this:
D72282 - [clang-tidy] Add bugprone-unintended-adl
[patch] Summary
This patch adds bugprone-unintended-adl which flags uses of ADL that are not on the provided whitelist.
bugprone-unintended-adl
Finds usages of ADL (argument-dependent lookup), or potential ADL in the case of templates, that are not on the provided lists of allowed identifiers and namespaces. [...]
.. option:: IgnoreOverloadedOperators
If non-zero, ignores calls to overloaded operators using operator
syntax (e.g. a + b), but not function call syntax (e.g.
operator+(a, b)). Default is 1.
.. option:: AllowedIdentifiers
Semicolon-separated list of names that the check ignores. Default
is
swap;make_error_code;make_error_condition;data;begin;end;rbegin;rend;crbegin;crend;size;ssize;empty.
.. option:: AllowedNamespaces
Semicolon-separated list of namespace names (e.g. foo;bar::baz).
If the check finds an unqualified call that resolves to a function
in a namespace in this list, the call is ignored. Default is an
empty list.
There seems to have been no activity on the patch since July 2020, though, but if this is of interest of the OP, the OP could try to resuscitate the patch.

Related

Using Qt's Q_DECLARE_FLAGS and Q_DECLARE_OPERATORS_FOR_FLAGS without Class Declaration

I have the following enum declaration and I'd like to make use of the QFlags support in Qt for extra type safety:
namespace ssp
{
enum VisualAttribute
{
AttrBrushColor = 0x001,
AttrBrushTexture = 0x002,
AttrPenCapStyle = 0x004,
AttrPenColor = 0x008,
AttrPenJoinStyle = 0x010,
AttrPenPattern = 0x020,
AttrPenScalable = 0x040,
AttrPenWidth = 0x080,
AttrSymbolColor = 0x100,
AttrTextColor = 0x200,
AttrTextFontFamily = 0x400,
AttrTextHeight = 0x800,
AttrAllFlags = 0xfff
};
Q_DECLARE_FLAGS (VisualAttributes, VisualAttribute)
Q_DECLARE_OPERATORS_FOR_FLAGS (VisualAttributes)
}
This declaration works for methods where I declare a VisualAttributes parameter and pass an OR'd list of values, so that part is fine, but it breaks (apparently) everywhere that other flags are used such as the Qt::WindowFlags. The compilation error I'm getting is:
error C2664: 'void QWidget::setWindowFlags(Qt::WindowFlags)' : cannot convert argument 1 from 'int' to 'Qt::WindowFlags'
No constructor could take the source type, or constructor overload resolution was ambiguous
The issue seems to be with the Q_DECLARE_OPERATORS_FOR_FLAGS declaration; if I remove it, the compilation issues with other flag types are resolved, but since this declares the operators for the flags, then the compiler won't accept the OR'd list. Including the declaration is resulting in some kind of ambiguous definition, but I don't understand what it is.
The QFlags documentation shows an example of embedding the enum into a class declaration, and that not only seems cumbersome, but made a bigger mess than what I'm already dealing with. I also looked at Qt's flag declarations (for Qt::AlignmentFlag), and I don't see that they're doing anything different than I am in the code segment above.
This is actually a very old Qt bug, that was fixed in Qt 5.12. As a general rule, due to argument dependent lookup, custom operators should be declared in the same namespace as their arguments. Back when Qt first introduced these flag enums and operators they chose to declare them in the global namespace, possibly due to poor namespace or argument dependent lookup support in compilers at that time.
So if one is a good, modern C++ citizen and declares one's custom operator| in the same namespace as its arguments, lookup fails to find the Qt operator| when compiling code in that same namespace. It finds an operator| in the current namespace that doesn't match and finds no operator| via argument dependent lookup. Due to complicated reasons I can't explain, the lookup then stops instead of searching the global namespace, where it would find Qt's operator|.
You can see a very simplified example of this in action here.
So you have three options:
Do what Qt (< 5.12) does and declare your custom operators in the global namespace, knowing that they might not be found by more modern C++ constructs.
Do what C++ best practices recommend and place your custom operators in the same namespace as their arguments and sprinkle your code with using ::operator|; to make the Qt enum operators compile.
Upgrade to Qt 5.12 or newer.
I was able to resolve this by moving the Q_DECLARE_OPERATORS_FOR_FLAGS declaration out of the namespace block, so it becomes:
Q_DECLARE_OPERATORS_FOR_FLAGS (ssp::VisualAttributes)
This resolved all of the compilation issues.

C++/ name conflict: how to exclude a previously defined function

I want to write log2() function for a new datatype that I defined myself Array. So it will look like this
#include <iostream>
#include <cmath>
Array log2(Array& A)
{
Array C=A;
for (int i=0; i<A.size(); i++)
C[i]=log2(A[i]);
return C;
}
despite other functions like sin, cos, etc, this one (log2) is not declared under std namespace. so even using the following
std::log2(A[i])
the compiler does not resolve that inside log2 is suppoed to be the built-in c function. I persist to use the same name (log2) for simplicity of the code.
This is the error message
error: invalid initialization of reference of type 'Array&' from expression of type 'double'
SOLVED: It worked when I switched to -std::C++ 11.
std::log2 was introduced in C++11. Make sure you have a C++11 compliant compiler (e.g. gcc4.8 or later, compile with -std=c++11), and use std::log2 inside your function.
If you don't use std::log2, then the compiler cannot find the standard function (as you are not using namespace std;) and tries to use yours, which of course is not defined for doubles, and you get an error.
My personal opinion is that you should try to avoid naming your function the same as a standard one, due to headaches that can later appear.
As far as I know, the built-in function log2 is not declared in namespace std.
You should use the following code to call the standard log2 function:
log2(A[i]);
I hope my answer helped.

c++11 tie name clash with boost

I am trying to migrate some code from boost::tuple to std::tuple but I'm getting some weird errors: after I invoke using namespace std (and never boost), I expect an unqualified tie to resolve to std::tie. However, this seems to fail when the tuple contains a boost container pointer, for example.
#include <tuple>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#ifdef USE_STD
#define TIE std::tie
#else
#define TIE tie
#endif
typedef boost::multi_index_container<
int,
boost::multi_index::indexed_by<
boost::multi_index::ordered_non_unique<
boost::multi_index::identity<int>
>
>
> Set;
std::tuple< int, int* > make_i_ptr();
std::tuple< int, Set* > make_set();
int main()
{
using namespace std;
int i;
int* i_ptr;
Set* set_ptr;
tie(i, i_ptr) = make_i_ptr();
TIE(i, set_ptr) = make_set();
return 0;
}
If I compile with g++ -std=c++0x -c test.cpp -DUSE_STD, all is well. However, without -D USE_STD, I get compile errors suggesting g++ tries to use boost::tuples::tie. I'm using g++ 4.8.1 and boost 1.55.0. Do you think this is a bug with boost? Or is there some spec I'm missing?
Lookup is complicated. The problem as others have mentioned is Argument Dependent Lookup or ADL. The rules for ADL were added to allow the definition of operators in the same namespace as the types they refer to, and enabling lookup to find those operators when present. This was later extended to all other functions in the process of standarization.
The issue here is that tie(...) is a function call. The compiler will attempt regular lookup from the point of use (inside main) it will move out to the enclosing namespace. The using directive will add ::std into the lookup search when it hits the root namespace (common ancestor of ::std and ::main). At that point, since the identifier resolves to a function, ADL will kick in.
ADL adds the namespaces associated to the arguments of the function call, which in this case is boost:: (fundamental types like int have no associated namespaces). At this point the compiler sees two declarations of tie: std::tie and boost::tie, causing ambiguity.
As you already know the solution is to qualify the call to std::tie (which I would advice that you use even without this issue). Regarding the comment:
If ADL makes it resolve to boost::tie for... "my convenience" and then the compilation fails, shouldn't that be a clue to the compiler that it picked the wrong function?!
I don't know what the exact error you are getting is (I don't use boost, and I don't know what possible overloads of std::tie it contains). If the problem is indeed one of ambiguity, the problem is that the compiler cannot resolve the identifier, and cannot continue the process. At that point it stops and asks for the programmer to resolve it. If the error is that it uniquely picked boost::tie and it later failed in the assignment, it means that theres is an overload of boost::tie that is a better match than std::tie and that was selected. At a later time the assignment from std::tuple may have failed, but the compiler cannot know whether the problem was during lookup, or whether it is the assignment itself (did you intend on assigning that variable? maybe a different one?) so again it fails and tells you what the problem is.
Note that in general the process of compilation is always moving forward, the compiler does not backtrack to double guess its own decisions*. There is a set of rules, and those rules are applied at each step. If there is ambiguity, compilation stops, if not, then there is a single best candidate and this point is resolved, moving to the next. Attempting to go back to undo decisions would turn the compilation process into something painfully slow (the number of paths that could be taken would be exponential).
* As always there are some exceptions but those are just exceptions, notably during overload resolution if a template is picked as the best candidate but substitution of type arguments fail, it is discarded and the next best candidate is chosen.

std::bind and winsock.h bind confusion

I'm working on a very large project and in one file we all of the sudden got a compile-time error where the compiler seems to think that our call to winsock.h bind() is actually a call to std::bind(). It seems that somewhere in a include file there is using namespace std code snippet. We could try and find where these using namespace std are in use and remove them, but perhaps there is a better way to do this?
You can change your calls to use ::bind() to specify the global namespace.
Yes, this is unfortunate. As I described at http://gcc.gnu.org/ml/libstdc++/2011-03/msg00143.html the std::bind template is a better match unless you use exactly the right argument types:
The problem is that the socket bind() function has this signature:
int bind(int, const sockaddr*, socklen_t);
so the call in the example using a non-const pointer finds that the
variadic template std::bind is a better match. The same would happen
if the third argument was any integral type except socklen_t.
Your code would work with GCC because I added a conforming extension to GCC's std::bind to prevent this ambiguity, by removing std::bind from the overload set if the first argument is "socket-like", which I defined using is_integral and is_enum. That doesn't help with other implementations though.
Removing the using namespace std; is a good idea anyway, but may not be entirely sufficient, because an unqualified call to bind() that happens to use a type defined in namespace std (such as std::size_t) could still find std::bind by argument dependent lookup. Jonathan Potter's answer is the best way to ensure you get the right function: qualify it as ::bind.

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.