简体   繁体   中英

how to overload empty std::initializer_list?

Here I have series of overloaded functions that have as input either vector or initializer_list. And I want to handle the special case when client code inputs empty initializer_list. The problem is that compiler cannot determine what data was supposed in such empty list. So my question is how I address in function declaration such case.

#include <string>
#include <vector>

using namespace std;
void func(vector<string> v) { }
void func(vector<wstring> v) { }
void func(initializer_list<string> iv) {}
void func(initializer_list<wstring> iv) {}

int main() {
  using namespace std;
  func({"apple", "banana"});
  func({L"蘋果", L"香蕉"});
  func({}); // special case
}

error message:

<stdin>: In function 'int main()':
<stdin>:14:10: error: call of overloaded 'func(<brace-enclosed initializer list>)' is ambiguous
<stdin>:14:10: note: candidates are:
<stdin>:5:6: note: void func(std::vector<std::basic_string<char> >)
<stdin>:6:6: note: void func(std::vector<std::basic_string<wchar_t> >)
<stdin>:7:6: note: void func(std::initializer_list<std::basic_string<char> >)
<stdin>:8:6: note: void func(std::initializer_list<std::basic_string<wchar_t> >)

void func(initializer_list<void> iv) {} - has no effect. I don't know how to properly declare it.

I'm hoping there's a better answer, but you can exploit the fact that list-initialization with empty braces of a defined type prefers a default constructor to an initializer_list constructor. Unfortunately, this means having to turn the overload of func into an overload set of constructors of a single parameter type:

using namespace std;
struct parameter {
   // representation left as an exercise - perhaps Boost.Variant?
   parameter(vector<string> v) {}
   parameter(vector<wstring> v) {}
   parameter(initializer_list<string> iv) {}
   parameter(initializer_list<wstring> iv) {}
   parameter() {}  // default constructor - func({}) calls this
};
void func(parameter) {}

If you weren't using string , you could use a template ie template<typename T> void func(initializer_list<T>) with an empty overload, as non-template overloads are preferred to template overloads, but unfortunately in your case the string literal calls are inferred as initializer_list<const char *> , which is difficult to convert to initializer_list<string> .

There's no way to distinguish this with the parameter alone. You can make one of them a template though, which will then be more costly and less preferable for overload resolution

void func(vector<string> v) { }
void func(vector<wstring> v) { }
template<typename = void>
void func(initializer_list<string> iv) {}
void func(initializer_list<wstring> iv) {}

Now calling func({}) will prefer the last function over the function template. Note that func({"hello", "world"}) still prefers the function template over the non-template vector-taking function, because the parameter conversion cost is more important than whether or not a candidate was synthesized from a template.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM