-1

I've recently learned that C++ can perform implicit function-to-function-pointer cast whenever it found necessary. For example, in my example below, my_func_1 and my_func_2 are equivalent. However, vector_1 and vector_2 are not, and in fact, vector_2 will give a compile error. Similar compile errors also happen for other standard containers, e.g. unordered_map. Why is it so?

class my_class {
    // dummy class
};

int main() {
    vector<my_class(*)()> vector_1; // correct one
    vector<my_class()> vector_2;    // gives compile error
    
    // However, both of the below are OK due to implicit
    // function-to-function-pointer conversion
    void my_func_1(my_class(*)());
    void my_func_2(my_class());    // equivalent to the one above
}

Here is the compile error message (shortened to highlight the error):

/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/ext/new_allocator.h:96:7: error: multiple overloads of 'address' instantiate to the same signature '__gnu_cxx::new_allocator<my_class ()>::const_pointer (__gnu_cxx::new_allocator<my_class ()>::const_reference) const noexcept' (aka 'my_class (*(my_class (&)()) const noexcept)()') address(const_reference __x) const _GLIBCXX_NOEXCEPT ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/allocator.h:112:30: note: in instantiation of template class '__gnu_cxx::new_allocator<my_class ()>' requested here class allocator : public __allocator_base<_Tp> ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/ext/alloc_traits.h:49:47: note: in instantiation of template class 'std::allocator<my_class ()>' requested here template<typename _Alloc, typename = typename _Alloc::value_type> ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_vector.h:83:35: note: in instantiation of default argument for '__alloc_traits<std::allocator<my_class ()>>' required here typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template ^~~~~~~~~~~~~~~~~~~~~~ /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_vector.h:386:30: note: in instantiation of template class 'std::_Vector_base<my_class (), std::allocator<my_class ()>>' requested here class vector : protected _Vector_base<_Tp, _Alloc> ^ Line 7: Char 24: note: in instantiation of template class 'std::vector<my_class (), std::allocator<my_class ()>>' requested here vector<my_class()> vector_2; // gives compile error ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/ext/new_allocator.h:92:7: note: previous declaration is here address(reference __x) const _GLIBCXX_NOEXCEPT ^

1
  • 3
    The conversion doesn't happen everywhere. As you noticed, it happens in function parameters, but not in template parameters. Commented Dec 14, 2020 at 20:20

1 Answer 1

3

In this declaration:

vector<my_class()> vector_2; 

the type used to instantiate the vector is myclass(), which is a function type. Specifically it's a function that takes no arguments, and returns a my_class.

A function type is not copy-assignable, as can be seen from this test:

static_assert(not std::is_copy_assignable_v<Foo()>);

Being copy-assignable is one of the requirements on the type used as a template parameter for std::vector, and so this declaration doesn't compile.


The remaining declarations are fine:

vector<my_class(*)()> vector_1; // vector of function pointer type

Due to implicit conversion to function-pointers of types used in function parameters, the following declarations are equivalent:

void my_func_1(my_class(*)());  // function taking a function pointer 
                                // to a function that takes no arguments
                                // and returns a my_class

void my_func_2(my_class());     // function taking a function that takes no 
                                // arguments and returns a my_class 
Sign up to request clarification or add additional context in comments.

18 Comments

No it is not a function type my_class() but it is just an r-value of type my_class.
Here is an example that proves that my_class() is not a function type: ` decltype(myclass()) a; a.foo();` we assume that my_class has a member function called foo then this call succeeds.
@Maestro No, it's a function type. my_class{} would be an r-value of type my_class. Compare the different error messages. Only the second one has a type/value mismatch.
Have you used decltype(my_calss())?
@Maestro Ok, definitely the last question I'm going to answer here :) No, it's exactly my_class because decltype will treat my_class() as an expression that is a direct-initialization of an object of type my_class. Of course, decltype is an unevaluated context so no object is actually created, but that is the type that you'll get.
|

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.