1
std::pair<Url, std::string> UrlParser::parse()
{
    return std::make_pair({ extract_scheme(), extract_hostname(), extract_port(),
                 extract_path(), extract_filename() }, host_ip_);
}

The host_ip_ variable is defined as

std::string host_ip_;

I get

UrlParser.cpp:91:64: error: no matching function for call to 'make_pair(<brace-enclosed initializer list>, std::string&)'
   91 |                  extract_path(), extract_filename() }, host_ip_);

The problem is on the host_ip_ variable. If it's an std::string, then what's the problem in returning it?

I found c++11 rvalue references in `std::make_pair` which explains that we can't call std::make_pair with non-rvalue references, so I tried

std::make_pair({ extract_scheme(), extract_hostname(), extract_port(),
                     extract_path(), extract_filename() }, std::move(host_ip_));

but I get

error: no matching function for call to 'make_pair(<brace-enclosed initializer list>, std::remove_reference<std::__cxx11::basic_string<char>&>::type)'
   91 |                  extract_path(), extract_filename() }, std::move(host_ip_));

By the way, why in the link provided, int is an rvalue reference but const int is not?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Guerlando OCs
  • 1,886
  • 9
  • 61
  • 150

1 Answers1

5

The problem has nothing to do with passing host_ip_ as lvalue or rvalue to std::make_pair; both should work fine. Instead, the braced-init-list { extract_scheme(), extract_hostname(), extract_port(), extract_path(), extract_filename() } makes template argument deduction for the 1st template paramter of std::make_pair failing because of non-deduced context.

  1. The parameter P, whose A is a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or a reference to an array:

You can pass a Url explicitly,

return std::make_pair(Url{ extract_scheme(), extract_hostname(), extract_port(),
//                    ^^^
             extract_path(), extract_filename() }, host_ip_);

or specify the template argument explicitly.

return std::make_pair<Url>({ extract_scheme(), extract_hostname(), extract_port(),
//                   ^^^^^
//                   specify the 1st template argument, left the 2nd one to be deduced
             extract_path(), extract_filename() }, host_ip_);
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • I couldn't find a reason for this thought. The compiler knows the type that needs to be returned, so it knows it's a pair where the first element is an Url. Why it's not possible? – Guerlando OCs Aug 22 '20 at 08:12
  • @GuerlandoOCs The problem is the calling of `make_pair` itself fails, template argument deduction won't consider return value. Consider if you call `make_pair` solely like `std::make_pair({ extract_scheme(), extract_hostname(), extract_port(), extract_path(), extract_filename() }, host_ip_);`, the types must be deduced by the calling itself, which has nothing to do with how the return value is assigned. – songyuanyao Aug 22 '20 at 08:20
  • nice, now I understand. C++ does not try to bind types into the places where they'll used. For example, if I return something, it does not try to look into the return type. This is a choice of design for the language. Rust on the other side does look and thus could infer the type – Guerlando OCs Aug 22 '20 at 08:51
  • @GuerlandoOCs Yes, allowing that means the behavior of same statement might change when the return value is used in different ways. That won't happen in C++. – songyuanyao Aug 22 '20 at 08:58