Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ADL with std::function: Can functions taking std::function objects be found via the types in the std::function's argument list?

Consider the following code snippet:

#include <functional>

namespace ns {
    struct Arg{};
    using Func = std::function<int(Arg)>;

    Func operator+(Func lhs, Func rhs) {
        return [lhs, rhs](Arg arg) {
            return lhs(arg) + rhs(arg);
        };
    }
}

int main() {
    ns::Func foo = [](ns::Arg i) {return 5;};
    ns::Func bar = [](ns::Arg i) {return 2;};
    auto foobar = foo + bar;
    return foobar(ns::Arg());
}

The above code compiles with various compilers. In contrast, the following code snippet does not compile. The only difference is the type of the argument used inside Func (Arg vs int):

#include <functional>

namespace ns {
    using Func = std::function<int(int)>;

    Func operator+(Func lhs, Func rhs) {
        return [lhs, rhs](int i) {
            return lhs(i) + rhs(i);
        };
    }
}

int main() {
  ns::Func foo = [](int i) {return i + 5;};
  ns::Func bar = [](int i) {return i * 2;};
  auto foobar = foo + bar; // BANG! Error here!
  return foobar(2);
}

I understand the error of the latter version: The called operator+ is defined in a namespace and thus not found without explicit specification of the namespace. Argument dependent lookup will not help here, because the operator+ is defined in a different namespace (ns) than the type of the argument (std::function is defined in namespace std, the using declaration is irrelevant for this).

But why is the correct operator+ found in the case where Func takes an argument ns::Arg? The namespace of Func has not changed. Is the code using Arg valid according to the C++ standard?

like image 760
user2296653 Avatar asked Aug 14 '19 14:08

user2296653


People also ask

How does ADL work C++?

In the C++ programming language, argument-dependent lookup (ADL), or argument-dependent name lookup, applies to the lookup of an unqualified function name depending on the types of the arguments given to the function call.

Why did we need to use an std :: function object?

It lets you store function pointers, lambdas, or classes with operator() . It will do conversion of compatible types (so std::function<double(double)> will take int(int) callable things) but that is secondary to its primary purpose.

What is C++ std :: function?

(since C++11) Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

What is the type of std :: function?

std::function is a type erasure object. That means it erases the details of how some operations happen, and provides a uniform run time interface to them. For std::function , the primary1 operations are copy/move, destruction, and 'invocation' with operator() -- the 'function like call operator'.


1 Answers

Is the code using Arg valid according to the C++ standard?

It is. The associated namespaces for ADL include the associated namespaces of any template argument for a specialization, according to [basic.lookup.argdep/2.2]

... Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces. — end note ]

std::function<int(Arg)> is a class template specialization, and ns is associated with one of its arguments. Therefore ns is included in the set of namespaces that is searched for operator+ by ADL.

This rule exists to make reusable components more useful. The idea is to allow a library to expose say an API that takes a std::unique_ptr<ns::Foo> as a handle type, and for ADL to pick up the correct functions from ns when presented with a handle.

like image 175
StoryTeller - Unslander Monica Avatar answered Sep 19 '22 04:09

StoryTeller - Unslander Monica