Объявление функции-члена с использованием синтаксиса типа функции

Совсем недавно я узнал, что вы можете объявить функцию (включая методы), используя переменный синтаксис с типом функции:

using function_type = int (double);
// pre-C++11:
//typedef int function_type(double);

function_type fun_global;

struct methods
{
    static function_type mem_fun_static;
    function_type mem_fun_normal;
    virtual function_type mem_fun_virtual;
    virtual function_type mem_fun_abstract = 0;
};

В приведенном выше коде

  • fun_global это глобальная функция,
  • mem_fun_static это static функция-член,
  • mem_fun_normal это обычный метод,
  • mem_fun_virtual это virtual метод,
  • mem_fun_abstract это абстрактный метод.

Все они принимают один аргумент типа double и вернуться int значение - так же, как function_type говорит.

Все эти годы я знал C++ и не знал об этом - этот язык не перестает меня удивлять! Кстати - этот синтаксис упоминается где-нибудь здесь? Я не вижу этого...

Однако, исследуя эту новую для меня функцию, я наткнулся на некоторые несоответствия между компиляторами. Для тестов я использовал следующие компиляторы:

  • GCC 5.4.0 и 7.1.0, командная строка: g++ -Wall -Wextra -pedantic -std=c++14
  • Clang 4.0.1, командная строка: clang++ -Wall -Wextra -pedantic -std=c++14
  • MSVC 19.10.25019 (VS 2017), командная строка: cl /W4 /EHsc

В тестах, которые я запускаю, обе версии GCC дали один и тот же результат, поэтому в дальнейшем я называю их GCC.


= delete несогласованность

struct methods
{
    /* ... */
    function_type mem_fun_deleted = delete;
};
  • GCC: ОК
  • Лязг: ошибка!

    Test.cpp:13:34: error: '= delete' is a function definition and must occur in a standalone declaration
            function_type mem_fun_deleted = delete;
                                            ^
    1 error generated.
    
  • MSVC: хорошо

= default несогласованность

struct methods
{
    /* ... */
    using assignment_type = methods& (methods const&);
    assignment_type operator= = default;
};
  • GCC: ОК
  • Лязг: ошибка!

    Test.cpp:14:30: error: '= default' is a function definition and must occur in a standalone declaration
            assignment_type operator= = default;
                                        ^
    1 error generated.
    
  • MSVC: ошибка!

    Test.cpp(14): error C2206: 'methods::operator =': typedef cannot be used for function definition
    

Несовместимость встроенного определения

struct methods
{
    /* ... */
    function_type mem_fun_inline { return 0; }
};
  • GCC: ошибка!

    Test.cpp:13:43: error: invalid initializer for member function ‘int methods::mem_fun_inline(double)’
      function_type mem_fun_inline { return 0; }
                                               ^
    Test.cpp:13:43: error: expected ‘;’ at end of member declaration
    
  • Лязг: ошибка!

    Test.cpp:13:33: error: expected expression
            function_type mem_fun_inline { return 0; }
                                           ^
    Test.cpp:7:8: error: missing '}' at end of definition of 'methods'
    struct methods
           ^
    /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/x86_64-pc-cygwin/bits/c++config.h:194:1: note: still within definition of 'methods' here
    namespace std
    ^
    2 errors generated.
    
  • MSVC: хорошо


Вопросы

Какие компиляторы здесь?

Кроме того, возможно ли:

  1. Во встроенном определении (поддерживается только MSVC) ссылаться как-то на аргумент?
  2. Каким-то образом использовать function_type также при определении этих функций (когда выполняется вне класса). Следующее в порядке (со всеми компиляторами)

    struct methods
    {
        static function_type mem_fun_static;
        /* ... */
    };
    
    int methods::mem_fun_static(double) { return 0; }
    

    Это не так плохо, так как смена function_type должен привести к ошибке компиляции при определении функции (так как она больше не будет соответствовать объявлению) - но все же, возможно, можно избежать даже этого.

1 ответ

Решение

§ 8.3.5 Функции [dcl.fct] p12 Определение типа функции может использоваться для объявления функции, но не должно использоваться для определения функции.

Таким образом, Clang вправе отклонить код во всех случаях, а компиляторы, которые его принимают, ошибаются.

(Цитата из N4618, но правило является частью языка с незапамятных времен).

Другие вопросы по тегам