4

I am unable to understand why does following function not compile

#include <iostream>
#include <map>

int main(){
  std::map<int, int, std::less<int>> myMap(std::less<int>());
  myMap[2] = 2;
  std::cout << myMap[2] << std::endl;
  return 0;
}

The error message is as follows -

std_less_check.cpp: In function ‘int main()’:
std_less_check.cpp:6:10: warning: pointer to a function used in arithmetic [-Wpointer-arith]
   myMap[2] = 2;
          ^
std_less_check.cpp:6:14: error: assignment of read-only location ‘*(myMap + 2)’
   myMap[2] = 2;
              ^
std_less_check.cpp:6:14: error: cannot convert ‘int’ to ‘std::map<int, int, std::less<int> >(std::less<int> (*)())’ in assignment
std_less_check.cpp:7:23: warning: pointer to a function used in arithmetic [-Wpointer-arith]
   std::cout << myMap[2] << std::endl;

while following compiles successfully

#include <iostream>
#include <map>

int main(){
  std::map<int, int, std::less<int>> myMap(std::less<int>{});
  myMap[2] = 2;
  std::cout << myMap[2] << std::endl;
  return 0;
}

Could someone please help me with this?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
atuly
  • 193
  • 1
  • 10
  • 1
    Fails to compile how exactly? What is the actual error message? There should be no difference between using `()` vs `{}` when constructing a `std::less` object. – Remy Lebeau Jan 25 '21 at 08:07
  • @RemyLebeau That's what I thought. But doesn't seem to be the case. I have edited the question with error message. – atuly Jan 25 '21 at 08:10
  • is there a reason why you are using `std::less` explicitly to begin with? It is already the default comparer for `std::map`, so you don’t need to use it explicitly in this code: `std::map myMap;` – Remy Lebeau Jan 25 '21 at 08:16
  • 2
    @RemyLebeau I am using std::less to reproduce the error. In my actual code, I am using a custom comparator. – atuly Jan 25 '21 at 08:20

1 Answers1

7

In the first program, you have a vexing parse. If the compiler can parse a declaration as either a variable or a function, it will choose to parse it as a function.

myMap can be parsed as a function declaration.

It returns a std::map<int, int, std::less<int>>.

It takes an argument of type std::less<int>(), which is itself a function type that returns a std::less<int> and takes no arguments. Note that you can't actually have a function type as an argument; the type is actually a pointer to a function that takes no arguments and returns a std::less<int>.


In the second program, replacing () with {} resolves the ambiguity. Now myMap can no longer be a function declaration, and so it instead declares a variable of type std::map<int, int, std::less<int>>.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • 1
    `takes an argument of type std::less()` Note that the parameter is adjusted to be a pointer to `std::less()` similar to how an array parameter would be adjusted to be a pointer to element of such array. – eerorika Jan 25 '21 at 08:14
  • @eerorika I'm not sure I follow. The parameter is a function type of the form `T()`, right? – cigien Jan 25 '21 at 08:18
  • 1
    A function is not an object; there are no values of function types. A function cannot be "passed by value" - just like an array cannot be "passed by value". When you write a function declaration with an array parameter, the parameter is adjusted to be a pointer (to element of such array). When you write a function declaration with a function parameter, the parameter is adjusted to be a pointer (to such function). As such, there are no functions that accept a function type nor functions that accepts an array type even though you can declare such functions. Such declarations are *adjusted*. – eerorika Jan 25 '21 at 08:20
  • @eerorika Oh, I see. Yes, that's true, but is it necessary to use that terminology? I think I've heard of functions being described as having array types and function types as parameters. Even though, as you point out, those types will get adjusted to pointers. – cigien Jan 25 '21 at 08:25
  • It's useful to mention it because not everyone reading the answer are aware of these adjustment rules. – eerorika Jan 25 '21 at 08:26
  • @cigien I understand there is an ambiguity between a function declaration and a variable. But wait, in C++, we can't declare functions within function. Does that not disambiguate it? – atuly Jan 25 '21 at 08:27
  • 1
    @atuly `in C++, we can't declare functions within function.` You've assumed wrongly. In C++, we **can** declare functions within function. – eerorika Jan 25 '21 at 08:29
  • 1
    @atuly Actually, in C++ you can't *define* a function inside a function, but you can declare it. – cigien Jan 25 '21 at 08:29
  • Oh I see. Could you please point me to some use case of declaring a function within a function but not defining it there? – atuly Jan 25 '21 at 08:30
  • @eerorika That's a good point. Added, thanks. – cigien Jan 25 '21 at 08:31
  • @atuly Here's a question https://stackoverflow.com/questions/1034606/is-there-any-use-for-local-function-declarations – cigien Jan 25 '21 at 08:58