0

I'm trying to bring a function into consideration with a using declaration:

namespace n {
    struct S {};
    bool equal(S a, S b, std::vector<int> = {1,2}) { return false; }
}

int main() {
    using ::n::equal;
    using ::n::S;
    /// ...
}

This works well as long as no std::initializer_list is passed as the third argument:

equal(S{},S{});
equal(S{},S{},{1});

But, it breaks when a std::initializer_list is passed as the third argument:

equal(S{},S{},std::initializer_list<int> {1, 2});

It only considers std::equal.

I struggle to understand the reason why this is not working, and to find a solution to enable this pattern.

live demo

5
  • 1
    Actually, {1} will be translated to a std::initializer_list<int> by the compiler. Which will then use it to create the temporary std::vector<int> object that is passed to the function. There's almost no use-case to create a std::initializer_list<T> object explicitly. Commented Aug 5, 2024 at 16:15
  • 2
    As for some of the errors you're getting, if you read it they say that the compiler tries to use std::equal. That's because of ADL (Argument-Dependent Lookup), which happens because one of the arguments (the initializer list object) is part of the std namespace. Commented Aug 5, 2024 at 16:19
  • 2
    Since you are passing a std::initializer_list, argument dependent lookup is choosing std::equal as your equal. The simplest solution for this is to call n::equal, rather than asking the compiler to choose which equal function to use. Commented Aug 5, 2024 at 16:19
  • A usual mistake - stop reading at error message notes, they often contain answers. Here <source>:11:5: note: in instantiation of function template specialization 'std::equal<n::S, std::initializer_list<int>>' requested here you are notified that not n::equal is used. Commented Aug 5, 2024 at 16:26
  • @Someprogrammerdude There's almost no use-case to create a std::initializer_list<T> object explicitly I'm not sure if my use case is sane. I'm just playing around trying to create a flag like enum which could be used with any set like type and initialize the sets like struct MyEnum { static constexpr int a = 1; static constexpr int b = 2; static constexpr auto ab = { a, b }; }; Commented Aug 5, 2024 at 16:29

1 Answer 1

7

I'm going to number these calls:

equal(S{},S{});                                    // #1
equal(S{},S{},{1});                                // #2
equal(S{},S{},std::initializer_list<int>{1, 2});   // #3

So in #1 and #2, the only viable candidate is n::equal, so it gets called and everything works.

But in #3, std::equal suddenly becomes a candidate - it's found by argument-dependent lookup (ADL) because of the explicit std::initializer_list argument. And there is an overload of std::equal that looks like this:

  template<class InputIterator1, class InputIterator2>
    constexpr bool equal(InputIterator1 first1, InputIterator1 last1,
                         InputIterator2 first2);

Note that while the template parameters are named InputIterator1 and InputIterator2, and those types really should be input iterators, the algorithm itself is not constrained, so it is considered to be a viable candidate.

And between n::equal and std::equal, the latter is a better match - all the arguments match exactly, whereas for n::equal an explicit conversion is required from std::initializer_list<int> to std::vector<int>. As such, std::equal is selected - which then won't compile because none of thse types are iterators.


The simplest solutions are to just not use ADL, or to not pass an explicit std::initializer_list<int> and just manually pass a std::vector<int>.

Alternatively, you could add an extra overload of n::equal to handle that case:

bool equal(S a, S b, std::vector<int> = {1,2});
bool equal(S a, S b, std::initializer_list<int> xs) {
    return equal(a, b, std::vector<int>(xs));
}

In constrast, std::ranges::equal is both constrained and cannot be found by ADL.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.